工厂模式
举例概述
需求:设计一个咖啡店点餐系统。
设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。
这个需求很简单,我们可以很快地写出如下的代码:
Coffee抽象类
public abstract class Coffee {
public abstract void getName();
public void addSugar() {
System.out.println("加糖");
}
public void addMilk() {
System.out.println("加牛奶");
}
}
美式咖啡子类
public class AmericanCoffee extends Coffee{
@Override
public void getName() {
System.out.println("美式咖啡");
}
}
拿铁咖啡子类
public class LatteCoffee extends Coffee{
@Override
public void getName() {
System.out.println("拿铁咖啡");
}
}
Coffee店
public class CoffeeStore {
public Coffee orderCoffee(String name) {
if ("american".equals(name)) {
return new AmericanCoffee();
} else if ("latte".equals(name)) {
return new LatteCoffee();
} else {
throw new RuntimeException("该店无此咖啡");
}
}
}
主测试方法
public class Test {
public static void main(String[] args) {
CoffeeStore coffeeStore = new CoffeeStore();
Coffee coffee = coffeeStore.orderCoffee("american");
coffee.getName();
}
}
需求一
现在产品经理来了,说要多一种咖啡的类型——卡布奇诺,你打开项目,添加Coffee子类,修改咖啡店的代码,添加了一句else if,发现工作量很小,并没有什么不妥的,虽然违反了开闭原则,但是我只是加了一句else if,维护工作不算大。于是你放心地早早交付任务了。
public class CoffeeStore {
public Coffee orderCoffee(String name) {
if ("american".equals(name)) {
return new AmericanCoffee();
} else if ("latte".equals(name)) {
return new LatteCoffee();
} else if ("cappuccino".equals(name)){
return new Cappuccino();
} else {
throw new RuntimeException("该店无此咖啡");
}
}
}
简单工厂模式
产品经理又来了,说有多家咖啡店要来合作,所以现在不止是一家CoffeeStore了,要一家美式咖啡店和一家意大利风味的咖啡店,并且还要再加一种新的咖啡类型,你这时就犯了难了,一家CoffeStore一份if else 的代码,多家岂不是多份一模一样的代码了?那么你思来想去,决定加一层,去解耦咖啡店和咖啡之间的关系,然后每家咖啡店都去引用中间一层即可,那么这就是简单工厂模式。
咖啡类都不需要更改,主要改动是咖啡店的代码,并加了一层中间的工厂
咖啡工厂代码
public class SimpleCoffeeFactory {
public Coffee createCoffee(String name) {
if ("american".equals(name)) {
return new AmericanCoffee();
} else if ("latte".equals(name)) {
return new LatteCoffee();
}else {
throw new RuntimeException("该店无此咖啡");
}
}
}
咖啡店
public class CoffeeStore {
public Coffee orderCoffee(String name) {
SimpleCoffeeFactory simpleCoffeeFactory = new SimpleCoffeeFactory();
Coffee coffee = simpleCoffeeFactory.createCoffee(name);
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
可以看到,现在咖啡店只需要通过工厂去创建咖啡,而选择创建哪一种咖啡的逻辑都放在了工厂去执行,如果现在多了几家咖啡店,也是一样,只需要获取工厂对象,然后创建咖啡即可。
对于工厂中的创建咖啡的代码,可以写为静态的方法,然后直接静态调用即可,该工厂也叫做静态工厂。
工厂方法模式
产品经理又来了,这次又推出了新品,继续让你加一种咖啡类型,你已经快要崩溃了,前前后后加咖啡类型已经几十次了,if else 已经写得满屏都是,你这时候想,能不能直接来多个工厂,然后每个创建一种咖啡,然后后台代码只需要添加新的类,然后修改的地方全部给客服端代码去做,这样就很好地符合了一个开闭原则,说完,就开始动手了,这就是工厂方法模式。
抽象工厂
public interface CoffeeFactoryMethod {
Coffee createCoffee();
}
工厂实现类
public class AmericanCoffeeFactory implements CoffeeFactoryMethod{
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
public class LatteCoffeeFactory implements CoffeeFactoryMethod{
public Coffee createCoffee() {
return new LatteCoffee();
}
}
咖啡店
public class CoffeeStore {
private CoffeeFactoryMethod coffeeFactory;
public void setCoffeeFactory(CoffeeFactoryMethod coffeeFactory) {
this.coffeeFactory = coffeeFactory;
}
public Coffee orderCoffee() {
Coffee coffee = coffeeFactory.createCoffee();
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
测试方法(客户端代码)
public class Test {
public static void main(String[] args) {
String name = "american";
CoffeeStore coffeeStore = new CoffeeStore();
CoffeeFactoryMethod coffeeFactory = null;
if ("american".equals(name)) {
coffeeFactory = new AmericanCoffeeFactory();
} else if ("latte".equals(name)) {
coffeeFactory = new LatteCoffeeFactory();
}
coffeeStore.setCoffeeFactory(coffeeFactory);
Coffee coffee = coffeeStore.orderCoffee();
coffee.getName();
}
}
现在,后台的代码只需要依次增加工厂和对应咖啡类型,然后其他需要修改的部分都是客户端的了。但是,类的爆炸性增长,也会增加系统的复杂度。
抽象工厂
前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机、传智播客只培养计算机软件专业的学生等。
而抽象工厂就是把同家店的东西全部聚合到一起,比如说,意大利工厂同时生产拿铁咖啡和提拉米苏,美国工厂同时生产美式咖啡和拿铁,然后两个工厂都实现抽象工厂接口。
美国工厂和意大利工厂(其实就是整合了简单工厂和工厂方法模式)
//美式甜点工厂
public class AmericanDessertFactory implements DessertFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
public Dessert createDessert() {
return new MatchaMousse();
}
}
//意大利风味甜点工厂
public class ItalyDessertFactory implements DessertFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
public Dessert createDessert() {
return new Tiramisu();
}
}
使用场景
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
-
系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
-
系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
如:输入法换皮肤,一整套一起换(logo、声音、颜色、效果、光效为同一家店生产的)。生成不同操作系统的程序。
反射+配置文件改进简单工厂
代码已经运行好一段时间了,但是你发现,不管怎么改,总是会有一段if else的判断逻辑。
if ("american".equals(name)) {
} else if ("latte".equals(name)) {
}
刚学了反射的你,你突然想到,难道就不能直接通过反射去创建对应的类吗?说着,你就动手实践了起来。
一、定义配置文件
我们先在resource目录下新建一个bean.properties的文件,里面就写你需要自动实例化的类的引用路径,文件内容如下:
american=shop.stopyc.patterns.factory.simplefactory.demo4.AmericanCoffee
latte=shop.stopyc.patterns.factory.simplefactory.demo4.LatteCoffee
二、改进工厂类
public class SimpleCoffeeFactory {
private static Map<String, Coffee> hashMap = new HashMap<>();
static {
Properties properties = new Properties();
Class<SimpleCoffeeFactory> aClass = SimpleCoffeeFactory.class;
InputStream is = aClass.getClassLoader().getResourceAsStream("bean.properties");
try {
properties.load(is);
for (Object key : properties.keySet()) {
String className = (String) properties.get(key);
Class<?> clazz = Class.forName(className);
Coffee coffee = (Coffee) clazz.newInstance();
hashMap.put((String) key, coffee);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Coffee createCoffee(String name) {
return hashMap.get(name);
}
}
三、使用
使用和简单工厂一模一样
public class CoffeeStore {
public Coffee orderCoffee(String name) {
Coffee coffee = SimpleCoffeeFactory.createCoffee(name);
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
public class Test {
public static void main(String[] args) {
CoffeeStore coffeeStore = new CoffeeStore();
Coffee coffee = coffeeStore.orderCoffee("latte");
coffee.getName();
}
}
通过反射+配置文件,直接砍去了if else的判断逻辑,现在只要和客服端约定key的值,然后客户端传入key,后台就能自动获取对应的实例。