这两天正在看关于多线程的一些内容,看到线程池的时候发现它的实现使用了工厂模式,之前对工厂模式的了解不深,只是知道他是根据需要创建对象的,索性就开个支线,找了本书看了看关于工厂模式的一些知识,书中讲的也比较有意思,以下是一些心得。
概述
对于设计模式来说,模式本身固然重要,但是模式设计的思想也同样很有味道,其中带来的一些OO的原则更是我们平时写代码需要注意的地方。而对于OO的设计原则其中有一个重要的思想就是将固定与变化分开,也就是简单的策略模式,将变化抽象,针对同一个接口,有各自的实现。
但是对于创建对象来说,java中只有new这一种方法,这就不可避免的要将代码写死,这又是我们不想看到的事情,由于硬编码带来的一系列拓展上的不便,使我们无法针对接口编程。就好像当我们使用集合的使用都会这么写:
1 | List<T> list = new XXXList<>(); |
因为这种针对接口的编程给了我们更多的自由。那么有没有一种灵活的方式创建对象,那就是工厂模式,所有的工厂模式都是针对对象的创建。
一.简单工厂
首先声明一下,简单工厂不是一种设计模式,只是一种习惯而已。他将动态的创建对象这一过程与固定的使用对象的代码分隔开。我们结合一个简单的例子来说:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
} else if (type.equals("clam")) {
pizza = new ClamPizza();
} else if (type.equals("veggie")) {
pizza = new VeggiePizza();
}
return pizza;
}
}
在这个例子中,我们所需要创建的对象是Pizza,但在这里我们通过一个factory代替了以往的new关键字来创建对象,而这样的好处也是显而易见的,在这个服务中,变化的是Pizza的种类,而处理Pizza 的流程是固定的。我们只需根据需要传入所需的factory,就能实现创建对象与使用对象的解耦。
我们通过定义一个工厂类,将创建对象的操作通过这个类来进行,当对象种类增加时,我们只需要修改工厂类,就是所谓类对修改关闭,对扩展开放。
二. 工厂方法
在上一个例子中,我们在PizzaStore中创建简单工厂对象,通过简单工厂创建对象,这不免让代码失去了一点弹性,让我们进一步抽象,将创建对象的方法进一步封装,形成一个抽象基类,让每个子类去各自实现自己所需的创建对象的方法。提高代码的可扩展性。下面给出例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28abstract class PizzaStore {
abstract Pizza createPizza(String item);
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
System.out.println("--- Making a " + pizza.getName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
public class ChicagoPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new ChicagoStyleCheesePizza();
} else if (item.equals("veggie")) {
return new ChicagoStyleVeggiePizza();
} else if (item.equals("clam")) {
return new ChicagoStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new ChicagoStylePepperoniPizza();
} else return null;
}
}
在上面的代码中,对象的创建只给出了一个抽象方法,而具体的实现,则有子类自由选择决定,这样极大的丰富了代码的选择性和扩展性。基类实际上并不知道他持有的是什么对象,他主要负责持有对象后的一系列固定流程操作。
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
对比工厂方法和简单工厂
原本有一个对象负责所有具体类的实例化,而在工厂方法中则由一些子类来负责实例化。工厂方法用来处理对象的创建,并将行为封装在子类,这样基类的代码就和子类的对象创建完全解耦。
三. 抽象工厂
当你需要创建的对象也依赖了一系列可变对象,那么就需要工厂模式中的最后一种方式–抽象工厂。我们首先给出抽象工厂的定义:
提供一个借口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
让我们再用Pizza来举例:
1 | public class ChicagoPizzaStore extends PizzaStore { |
PizzaStore和之前一样,这里就不重复了,和之前不一样的是在子类的createPizza方法中,我们不是简单的返回对象,而是根据创建对象所依赖的成员的不同,也进行了“个性化定制”。
其本质上其实也是用工厂方法对依赖对象进行创建。
四. 抽象工厂与抽象方法的比较
- 工厂方法使用继承,把对象的创建委托给子类,子类实现工厂方法来创建对象,并将实例化延迟到子类。
- 抽象工厂使用组合,对象的创建被实现在工厂接口所暴露出来的方法中。
- 抽象工厂创建相关的对象家族,并让他们集合起来,而不需要依赖他们的具体类
五. References
《Head First 设计模式》