目录
1. 简单工厂
1.1 定义
- 简单工厂更像是一种编程习惯而非一种设计模式,其主要是用于将创建对象的过程进行封装; 通过多态的特性在运行时由子类决定实例具体的对象。
- 工厂模式通常分为简单工厂、工厂方法、抽象工厂这三种,通常情况比较常用的有简单工厂与工厂方法。
1.2 简单工厂样例代码
简单工厂模式通过代码可以相当直接的看出个所以然,所以下文直接以样例来说明其相关特性,以下例子总结自HeadFirst设计模式一书;
假设有一个比萨店,创建不同类型比萨的过程都需要经过prepare、bake、cut、box等过程,那么则可以设计一个抽象的Pizza超类,其他不同类型的比萨继承自该超类并根据自身的需要决定是否需要重写超类的方法。
public class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = null;
// 只有当代码运行到这里才知道该实例化为哪一个类对象
if ("cheese".equals(type)) {
pizza = new CheesePizza();
} else if ("greek".equals(type)) {
pizza = new GreekPizza();
} else if ("pepperoni".equals(type)) {
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
- 如上述代码中根据type类型而创建指定的pizza对象正是结合了抽象与继承特性很好地应用了多态的特性。这也是“针对接口编程而非实现编程”的一种体现。
- 当需要添加或删除某一类型的比萨,则需要修改PizzaStore 类中的if条件,这违反了设计原则之“对扩展开发,对实现封闭”。简单地来说,当我们花了很多时间将orderPizza(type)调试好后,我们不想频繁地因为引入新变化而重新再花大量时间进行调试,当然这里的例子很简单因而不太能体现该思想。对代码进行优化通常有一思想“分离出改变的部分,封装非改变部分”。
由上面的例子知道,根据type创建比萨是改变的部分,因此可将其抽取出来放到一个专门负责创建比萨的对象中,该对象也就是简单工厂对象。
- 简单工厂对象代码如下:
public class SimpleFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new CheesePizza();
} else if ("greek".equals(type)) {
pizza = new GreekPizza();
} else if ("pepperoni".equals(type)) {
pizza = new PepperoniPizza();
}
return pizza;
}
}
- 运用工厂后的比萨店代码
public class PizzaStoreSF {
SimpleFactory factory;
public PizzaStoreSF(SimpleFactory factory){
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
* 当有该商店需要添加或删除某比萨时,只需要往该类中createPizza方法添加对应的逻辑以及添加一个继承自Pizza类。这样也就实现对扩展开放,对修改封闭。当然如果从每次都要修改工厂类来说算是有些违背了开闭原则,但是如果对于逻辑不算复杂的工厂类来说还是可以接受的。
* 优点:所有的业务代码都集中在工厂类中,代码逻辑简单
* 缺点:当业务特别复杂后,该简单工厂类便难以维护,这里再次运用多态特性对该工厂进行抽取(也就是工厂方法模式)
1.3 UML类图
- 1. PizzaStore类似于客户端,通过工厂类进行创建具体对象;
- 2. 工厂类的创建对象方法通常被定义成静态方法,以方便工厂类直接调用;
- 3. Pizza超类可定义成接口或抽象类,其所有具体类需要实现或继承自该超类,要不然工厂类不能通过多态的方式进行运行时动态实例化对象。
1.3 语法特点
运用了继承、封装、多态、接口等编程特性,以及对扩展开放,对修改封闭、基于接口编程而非实现编程等思想。
1.4 应用场景
当创建具体对象的业务逻辑不太复杂,也就是可以容忍工厂类中有多个类似 if 分支的情况下可直接使用简单工厂模式(能容许工厂类在一定程度上不符合开闭原则);如果非要将这些if分支进行优化,或者是说当业务变得更加复杂后,则就需要引入工厂方法模式了。
1.5 简单工厂在JDK源码中的应用
2. 工厂方法模式
2.1 定义
定义了一个创建对象的接口,但是由子类决定实例化的类是哪一个,其将类的实例化推迟到子类。
2.2 工厂方法的示例代码
从简单工厂类代码来说,当每添加都删除一个类型的比萨就得修改该代码,以及当添加的类型很多后,该代码也会变得很复杂。
因此要做的就是通过多态优化以下代码,个人感觉这有点像对简单工厂进行再一次简单工厂的处理。
优化的思路是,重新定义一个创建工厂超类(抽象或接口),该工厂超类定义了创建对应工厂的方法,然后各个比萨类型定义成工厂类(继承工厂超类并实现同样的创建工厂方法)。
示例代码如下:
- 1. 工厂超类
public interface PizzaFactory {
Pizza createPizza(String type);
}
- 2. 对应产品所对应的工厂类
public class GreekPizzaFactory implements PizzaFactory {
@Override
public Pizza createPizza(String type) {
// TODO Auto-generated method stub
return new GreekPizza();
}
}
注:其他所有比萨都类似继承自工厂超类并实现对应的创建比萨方法。
- 3. 应用简单工厂方法后的比萨商店代码
public class PizzaStore {
public Pizza orderPizza(String type) {
PizzaFactory factory = null;
if ("cheese".equals(type)) {
factory = new CheesePizzaFactory();
} else if ("greek".equals(type)) {
factory = new GreekPizzaFactory();
} else if ("pepperoni".equals(type)) {
factory = new PepperoniPizzaFactory();
}
Pizza pizza = factory.createPizza(type);
System.out.println("Pizza type class is " + pizza.getClass());
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
我们注意到上述代码似乎又回到了原先没有应用工厂方法前了,存在多个if分支,因此可对上述代码进行一定程度的优化,通过多态将if分支替换掉;
-
4. 简单工厂方法的优化版
创建存储比萨及其对应比萨工厂类的映射类,代码如下
public class PizzaFactoryMap {
private static final HashMap<String, PizzaFactory> factoryCatch = new HashMap<String, PizzaFactory>();
static {
factoryCatch.put("cheese", new CheesePizzaFactory());
factoryCatch.put("greek", new GreekPizzaFactory());
factoryCatch.put("pepperoni", new PepperoniPizzaFactory());
}
static PizzaFactory createFactory(String type) {
if (type.isEmpty() || type == null) {
return null;
}
return factoryCatch.get(type.toLowerCase());
}
}
- 类说明:先将具体对象的工厂实例化至内存中,当用到时直接往内存中取,提高了性能
- 5.优化后的比萨商店如下
public class PizzaStore2 {
public Pizza orderPizza(String type) {
PizzaFactory factory = PizzaFactoryMap.createFactory(type);
Pizza pizza = factory.createPizza(type);
System.out.println("Pizza type class is " + pizza.getClass());
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
public static void main(String[] args) {
PizzaStore2 store = new PizzaStore2();
store.orderPizza("greek");
}
}
- 代码小结:
- 通过上述优化后的代码,当需要添加新类型的比萨(删除某一类比萨同理),对应的比萨商店代码不需要做任何改动,也就是说原先经过辛辛苦苦调试成功的相关业务代码无需变化。
- 需要进行改动的部分只有两个地方:
- 添加对应比萨工厂类(该比萨工厂类继承工厂超类并实现其对应的创建比萨的方法);
- 将对应的比萨类加入到比萨缓存类PizzaFactoryMap 中。
2.3 UML类图
- 1. 工厂超类定义了创建对应产品的接口;
- 2. 对应产品的工厂类继承自工厂超类并实现了其创建实际产品的方法;
- 3. 产品超类定义了抽象产品的方法,并由具体的产品类继承与实现对应产品内部的方法;
上述这些继承与实现其实最终主要是方便应用多态这一特性实现类把实例化推迟到子类。
下图引自Head First工厂方法的类图