DomBro Studio

Java工厂设计模式

2017/12/11

目录

0.阅读指南

初次接触工厂模式的同学,如果不想看枯燥的概念,可以先从3.1引子看起,回过头来在看一下概念性较多的What部分。

1.前言

当老师讲设计模式这门课整天嘴上挂着面向对象、OOP等专业术语,觉得离自己太遥远。总以为技术上的low逼怎么可能理解或者用到 GoF 的《设计模式》这本书…然鹅事实并非如此。上课迷迷糊糊听到老师讲到工厂模式,听到了Factory 这个单词,我马上被吸引了,Spring、MyBatis里面都有Factory…莫非有啥关系吗?好好看过书与上网查阅资料后,才发现设计模式离我如此之近!!!

2.What

在 GoF 四位大佬写的《设计模式》(被面向对象编程人员当做圣经的一本书)创建型模式这一章中,提到了五种创建型模式,这其中包括抽象工厂模式(ABSTRACT FACTORY)工厂方法模式(FACTORY METHOD)
创建型模式,顾名思义创造对象实例化过程的模式,也就是帮你造对象的模式,主要的功能就是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性。虽然四大佬在书里只提到两种创建型工厂模式,其实还有一种Java语言常用的简单工厂模式。今天就来撸一撸这三种工厂模式!

2.1 简单工厂模式

简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。不修改代码的话,是无法扩展的

2.2 工厂方法模式

工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。在同一等级结构中,支持增加任意产品。

2.3 抽象工厂模式

抽象工厂是应对产品族概念的。应对产品族概念而生,增加新的产品线很容易,但是无法增加新的产品。

3. How

事实上这三种工厂模式并没有严格的好坏之分,会因为使用场景的不同而有最适合的模式。

3.1 引子

在面向对象的世界中,最高阶的功法与核心就是模拟真实世界,把真实世界中的事物抽象成类,整个程序靠各个类的实例互相通信、互相协作完成系统功能,这非常符合真实世界的运行状况,也是面向对象思想的精髓。
本着这种精髓来举个例子,有这样一个人,就叫他大黄吧,他想要驾驶一辆汽车兜兜风,他可能会这样:

  • 剧本一 大黄自己造汽车
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
											产品
//汽车类
class Car{

public void move(){
System.out.println("我只是一辆普通的小汽车,我开动了")
}

}


使用者:

//大黄先生类(调用类)
public class DaHuang{

public void drive(){
//自己造汽车,累的一批
Car car = new Car();
//启动车
car.move();
}

}

有没有很累呀,大黄先生只想安静的开车兜风,但却要自己亲自造一个车(new Car) 。太不suang了,后来大黄先生发现家附近有一个汽车工厂,不仅可以造汽车,还可以选择宝马和奔驰这两种品牌,这可乐坏了大黄先生,于是:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
			                 	          产品
//汽车接口
interface Car{

public void move();

}

//宝马汽车类
class Bwm implements Car{

public void move(){
System.out.println("我可是宝马老铁,我开动了,速度可快了呢!")
}

}

//奔驰汽车类
class Benz implements Car{

public void move(){
System.out.println("我是奔驰呀,我开动了,我的稳定性很好")
}

}

工厂:

class CarFactory{

public static Car createCar(String carBrand){
switch(carBrand){
case "Bwm":
return new Bwm();
break;
case "Benz":
return new Benz();
break;

}
}

}


使用者:

//大黄先生类(调用类)
public class DaHuang(){
//依赖汽车接口
private Car car;

public void drive(){
//找工厂要指定型号的汽车
car = CarFactory.createCar("Bwm");
//启动车
car.move();
}

大黄先生开着从工厂造出来的宝马,心里想那以后开奔驰的时候还要特意跑来工厂跟厂长说我只想要奔驰,靠,我只想开车呀!可不可以在省力一点!就在这个时候厂长打来电话,告诉大黄先生,我们厂改革啦,造奔驰和造宝马的分成两个不同的厂子啦,以后给指定的厂子打电话就可以送货上门!真的吗?大黄先生高兴坏了:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
										产品
//汽车接口
interface Car{

public void move();

}

//宝马汽车类
class Bwm implements Car{

public void move(){
System.out.println("我可是宝马老铁,我开动了,速度可快了呢!")
}

}

//奔驰汽车类
class Benz implements Car{

public void move(){
System.out.println("我是奔驰呀,我开动了,我的稳定性很好")
}

}


工厂:

//工厂改革了,不在这个厂区生产了,只进行图纸研发,即变成接口,只规定一些规范,
interface CarFactory{

public Car createCar();

}

//宝马汽车厂
class BwmFactory implements CarFactory{

private BwmFactory(){

}

//单例模式,即只提供送货上门
public static BwmFactory getInstance(){
return BwmFactory();
}


//只生产宝马
public Car createCar(){
return new Bwm();
}
}


//奔驰汽车厂
class BenzFactory implements CarFactory{

private BenzFactory(){

}
//单例模式,即只提供送货上门
public static BenzFactory getInstence(){

return new BenzFactory();
}

//只生产宝马
public Car createCar(){
return new Benz();
}

}




使用者:

//大黄先生类(调用类)
public class DaHuang(){
//依赖汽车接口
private Car car;
public void drive(){
//直接给生产奔驰的厂区打电话,叫他们把奔驰送过来
car = BenzFactory.getInstence().createCar();
//启动车
car.move();
}

这下大黄舒服满意了吧?他开着厂家送过来的奔驰,突然发现自己的车没有安装行车记录仪,大黄当然自己可以造一款行车记录仪(new Tachograph),但是大黄只想开车,于是大黄开车找到了行车记录仪厂家,当然可以帮大黄安装,但大黄只想开车呀,各位乡亲父老,他只想开车有错吗?这时候,汽车厂厂长又打来了电话,告诉大黄先生说,我们厂的业务扩充到行车记录仪的领域啦,以后给下面分厂打电话就可以把装有指定品牌的行车记录仪的奔驰或宝马 送货上门啦!大黄先生默默拨通了电话:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
												产品
//汽车接口
interface Car{

public void move();

}

//宝马汽车类
class Bwm implements Car{

public void move(){
System.out.println("我可是宝马老铁,我开动了,速度可快了呢!")
}

}

//奔驰汽车类
class Benz implements Car{

public void move(){
System.out.println("我是奔驰呀,我开动了,我的稳定性很好")
}

}

//行车记录仪接口
interface Tachograph(){
public void record();
}

//A品牌行车记录仪
class TachographA() implements Tachograph{
public void record(){
System.out.println("我是A品牌行车记录仪,我开始记录了")
}
}

//B品牌行车记录仪
class TachographB() implements Tachograph{
public void record(){
System.out.println("我是B品牌行车记录仪,我开始记录了")
}
}



》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
工厂

//工厂升级了,不止生产汽车,还生产行车记录仪
interface CarAndTachographFactory{

public Car createCar();

public Tachograph creatTachograph();

}


//既生产宝马又生产A品牌行车记录仪的工厂
class BwmAndTachographAFactory(){

private BwmAndTachographAFactory(){

}
//单例模式,即只提供送货上门
public static BwmAndTachographAFactory getInstence(){

return new BwmAndTachographAFactory();
}


public Car createCar(){
return new Bwm();
}

public Tachograph creatTachograph(){
return new TachographA();
}

}

//既生产奔驰又生产B类行车记录仪的工厂
class BenzAndTachographBFactory(){

private BenzAndTachographBFactory(){

}
//单例模式,即只提供送货上门
public static BenzAndTachographBFactory getInstence(){

return new BenzAndTachographBFactory();
}

public Car createCar(){
return new Benz();
}

public Tachograph creatTachograph(){
return new TachographB();
}

}

》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
使用者

//大黄先生类(调用类)
public class DaHuang(){
//依赖汽车接口
private Car car;
//依赖行车记录仪接口
private Tachograph tachograph;

public void drive(){
//给生产奔驰和A品牌行车记录仪的厂家打电话,叫他们把奔驰车送来
car = BenzAndTachographBFactory.getInstence().createCar();
//把A品牌行车记录仪也送过来
tachograph = BenzAndTachographBFactory.getInstence().creatTachograph();
//启动车
car.move();
//记录
tachograph.record();
}

}

这样一来,大黄就不用再把注意力放在如何得到汽车和行车记录仪,只需要给生产指定产品组合的工厂 打call ,等待工厂为他造好就可以了。

3.2 Why

看了上面的引子和概念,没错想必你还是一脸的懵逼。这里特意准备了几个问题,供你参考。

  • 看了这么多,所以为什么我们不要自己去 new 对象,反而要写这么多啰嗦的代码?

(1).就像引子中的多次强调的,当一个调用者的关注点仅仅是调用产品中的方法,而不想亲自创建这个具体产品的对象时,通过工厂模式可以让调用模块更加规范专注于结构上的编码。
(2). 实际上当我们在程序中,使用了 new 关键字,就会在内存中开辟一块地址空间,这个看似简单的过程,要经过很多的判断,例如要生成哪个子对象实例,是否需要计算或取得对象的初始设置,或在生成你需要的对象之前必须先生成一些辅助功能的对象等等判断,这不就好像一个造汽车的过程吗?
(3). 合作开发时的考虑,有的时候需要多人协作来完成一些项目,不同人员那肯定是写不同层次的代码如,底层,应用层,调用层,当编写调用层的人想要去获取应用层是实例,那就尴尬了,应用层根本不是他写的呀,擅自实例化肯定有危险,所以写应用层的人要为他提供工厂,让调用层实例。

  • 直接去弄一个Factory把需要实例化的具体产品实例化不就好了吗?

这真的是一个好问题,也困扰了我很久。但实际上确实有问题中的这种工厂类(反射机制)。以下是我自己的见解,我认为所说的三种工厂设计模式是通过多态去实例化一类的产品,想一下如果你每实例化一个产品(注意是一个),都在万能类中去反射获得一个实例,你需要传入这个产品的具体类名,那这和你去 new 一个对象有什么区别? 因为这个具体的产品(类名)还是在调用的时候被暴露了!

  • 怎么有那么多的接口(抽象类)?工厂接口,产品接口…?

为什么会有这么多的接口,实际上一提到接口就一定会想到多态。如引子里面所描述的产品宝马汽车和奔驰汽车都是汽车这一类产品(产品接口),而工厂接口则进行了一个规定,即我的实现类或子类,必须去生产汽车这类产品,加入以后汽车产品新增了奥迪汽车,那只需要在实现一个奥迪汽车工厂用来生产奥迪就好了。保证了我们程序的可扩展性。

  • 为什么调用者只依赖于抽象的产品接口?

实际上这是面向接口百年城的内容,这个问题和上一个问题可以一起回答,上一个问题是为什么有产品接口和工厂接口,我可以用一个词来回答你!多态!多态!还他md是多态!想一下要是不依赖于产品接口,那你的调用者要依赖一个甚至是多个具体的产品,并且一旦这个具体的产品有所改动,那整个调用者都会跟着改动。在一定程度上解耦合,依赖接口还不依赖具体实现,在替换实现类的时候,可以将影响减到最小。

4. When

说了这么多,这三种工厂模式什么时候使用呢?在 MVC 设计模式下,service 与 DAO 不正好是调用者和产品的关系吗? service 层并不在乎 DAO 如何创建,却需要 DAO 中的方法。 而设计 DAO 的方式多种多样,举几个小例子

1
2
3
4
5
6
7
8
9
//一个DAO接口,需要子类实现增删改查操作
interface DAO{

boolean updata(arg...);
boolean insert(arg...);
boolean list<Object> get(arg...);
boolean Object<Object> getList(arg...);

}
  • 简单工厂

我们知道,简单工厂模式一般只有一个具体工厂类,所以当具体产品较少时,适合使用简单工厂。

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
//只有两个具体实现
class StudentDAOImp implements DAO{
略....
}

class BookDAOImp implemets DAO{
略....
}

//工厂接口
interface IDAOFactory(){
static DAO getDAO(String target);
}
//简单接口

class DAOFactory(){
static public DAO getDAO(String target){
switch(target){
case "stu" :
return new StudentDAOImp();
break;
case "book" :
return new BookDAOImp();
break;
}
}
}
  • 工厂方法

实际简单工厂是上工厂方法的特例,所以当你认为你的某类DAO产品有点多,并且很有可能还会新增这类DAO的具体实现,那就使用工厂方法模式,为该类DAO的每个具体实现派生具体工厂类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class StudentDAOFactory(){
static public DAO getDAO(String target){

return new StudentDAOImp();

}
}

class BookDAOFactory(){
static public DAO getDAO(String target){

return new BookDAOImp();

}
}
  • 抽象工厂

实际上,有些时候我们的可能DAO并没有那么简单,发现并不是单纯的增删改查就万事大吉,针对不同的表有不同的操作,比如 StudentDAO 和 BookDAO 并抽取不出太多相同的操作。那这个时候 StudentDAO 和 BookDAO 就属于两种产品类,但却属于同一产品族。

1
2
3
4
5
6
7
8
9
10
//如下,这是两个不同的产品
interface StudentDAO{
List<Student> getStudents(args...);
List<Student> findById(int id);
}

interface BookDAO{
int getBookId(args...);
boolean deleteBook(int args);
}

针对不同的DAO产品,那具体DAO产品的实现肯定不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//使用JDBC实现的StudentDAO
class StudentDAOByJdbc implements StudentDAO{

略...
}

//使用MyBatis框架实现的StudentDAO
class StudentDAOByMyBatis implements StudentDAO{

略...
}

//使用JDBC实现的BookDAO
class BookDAOByMyBatis implements BookDAO{

略...
}

//使用MyBatis框架实现的BookDAO
class BookDAOByMyBatis implements BookDAO{

略...
}

重点在于抽象工厂

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
interface IDAOFactory {

StudentDAO getStudentDAO();
BookDAO getBookDAO();

}


class JdbcDAOFactory implements IDAOFactory{
StudentDAO getStudentDAO(){
return new StudentDAOByJdbc();
}
BookDAO getBookDAO(){
return new BookDAOByJdbc();
}
}


class MyBatisDAOFactory implements IDAOFactory{
StudentDAO getStudentDAO(){
return new StudentDAOByMyBatis();
}
BookDAO getBookDAO(){
return new BookDAOByMyBatis();
}
}

事实上,当然不会有人写直接在程序中两套DAO接口,但是当我们遇到更好的DAO解决办法(比如有Jdbc升级到MyBatis),如果再去修改原有的DAO实现,是一件很恐怖的事情,所以可以将将新写好的一套具体DAO的实现封装在实现了抽象工厂的具体工厂中,在调用层代用这个工厂中的DAO实例就OK了!!

5. 总结

  • 总结

1.工厂模式中,重要的是工厂类,而不是产品类。产品类可以是多种形式,多层继承或者是单个类都是可以的。但要明确的,工厂模式的接口只会返回一种类型的实例,这是在设计产品类的时候需要注意的,最好是有父类或者共同实现的接口。

2.使用工厂模式,返回的实例一定是工厂创建的,而不是从其他对象中获取的。

3.工厂模式返回的实例可以不是新创建的,返回由工厂创建好的实例也是可以的。

4.简单工厂 : 用来生产同一等级结构中的任意产品。(对于增加新的产品,无能为力)

5.工厂方法 :用来生产同一等级结构中的固定产品。(支持增加任意产品)

6.抽象工厂 :用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)

7.以上三种工厂 方法在等级结构和产品族这两个方向上的支持程度不同。所以要根据情况考虑应该使用哪种方法。

8.简单工厂优点:客户端可以免除直接创建产品对象的责任,而仅仅是“消费”产品。简单工厂模式通过这种做法实现了对责任的分割。

9.工厂方法有点:允许系统在不修改具体工厂角色的情况下引进新产品。

10.抽象工厂优点:向客户端提供一个接口,使得客户端在不必指定产品具体类型的情况下,创建多个产品族中的产品对象

6. 参考

图书:
GoF《设计模式》

博客:
http://zyjustin9.iteye.com/blog/2094960

http://blog.csdn.net/hguisu/article/details/7505909

http://blog.csdn.net/u014600626/article/details/52131016

http://blog.csdn.net/u013604031/article/details/50595107

CATALOG
  1. 1. 目录
  2. 2. 0.阅读指南
  3. 3. 1.前言
  4. 4. 2.What
    1. 4.1. 2.1 简单工厂模式
    2. 4.2. 2.2 工厂方法模式
    3. 4.3. 2.3 抽象工厂模式
  5. 5. 3. How
    1. 5.1. 3.1 引子
    2. 5.2. 3.2 Why
  6. 6. 4. When
  7. 7. 5. 总结
  8. 8. 6. 参考