简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出那一种产品类的实例。
1.工厂模式的几种形态
工厂模式专门负责将大量有共同接口的类实例化。工厂模式可以动态决定将哪一个类实例化,不必事先知道每次要实例化哪一个类。工厂模式有以下几种形态:
(1)简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory Method Pattern)。
(2)工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式;
(3)抽象工厂(Abstract Factory)模式,又称工具箱(Kit 或Toolkit)模式。下面就是简单工厂模式的简略类图。
简单工厂模式,或称静态工厂方法模式,是不同的工厂方法模式的一个特殊实现。在其他文献中,简单工厂往往作为普通工厂模式的一个特例讨论。
学习简单工厂模式是对学习工厂方法模式的一个很好的准备,也是对学习其他模式,特别是单例模式和多例模式的一个很好的准备。
2 .简单工厂模式的引进
比如说有一个农场公司,专门向市场销售各类水果。在这个系统里需要描述下列的水果:
葡萄 Grape
草莓 Strawberry
苹果 Apple
水果与其他的植物有很大的不同,就是水果最终是可以采摘食用的。那么一个自然的作法就是建立一个各种水果都适用的接口,以便与农场里的其他植物区分开。如下图所示。
水果接口规定出所有的水果必须实现的接口,包括任何水果类必须具备的方法:种植plant(),生长grow()以及收获harvest()。接口Fruit 的类图如下所示。
这个水果接口的源代码如下所示。
代码清单1:接口Fruit 的源代码
1
2
3
4
5
6
|
interface Fruit { public function grow(); public function harvest(); public function plant(); } |
Apple 类是水果类的一种,因此它实现了水果接口所声明的所有方法。另外,由于苹果是多年生植物,因此多出一个treeAge 性质,描述苹果树的树龄。下面是这个苹果类的源代码。
代码清单2:类Apple 的源代码
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 Apple implements Fruit { private $_treeAge ; public function grow() { echo "Apple is growing." ; } public function harvest() { echo "Apple has been harvested." ; } public function plant() { echo "Apple has been planted." ; } public function getTreeAge() { return $this ->_treeAge; } public function setTreeAge( $treeAge ) { $this ->_treeAge = (int) $treeAge ; } } |
同样,Grape 类是水果类的一种,也实现了Fruit 接口所声明的所有的方法。但由于葡萄分有籽和无籽两种,因此,比通常的水果多出一个seedless 性质,如下图所示。
葡萄类的源代码如下所示。可以看出,Grape 类同样实现了水果接口,从而是水果类型的一种子类型。
代码清单3:类Grape 的源代码
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 Grape implements Fruit { private $seedless ; public function grow() { echo "Grape is growing." ; } public function harvest() { echo "Grape has been harvested." ; } public function plant() { echo "Grape has been planted." ; } public function getSeedless() { return $this ->seedless; } public function setSeedless( $seedless ) { $this ->seedless = (boolean) $seedless ; } } |
Strawberry 类实现了Fruit 接口,因此,也是水果类型的子类型,其源代码如下所示。
代码清单4:类Strawberry 的源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Strawberry implements Fruit { public function grow() { echo "Strawberry is growing." ; } public function harvest() { echo "Strawberry has been harvested." ; } public function plant() { echo "Strawberry has been planted." ; } } |
农场的园丁也是系统的一部分,自然要由一个合适的类来代表。这个类就是FruitGardener 类,其结构由下面的类图描述。
FruitGardener 类会根据客户端的要求,创建出不同的水果对象,比如苹果(Apple),葡萄(Grape)或草莓(Strawberry)的实例。而如果接到不合法的要求,FruitGardener 类会抛出BadFruitException 异常。
园丁类的源代码如下所示。
代码清单5:FruitGardener 类的源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class FruitGardener { public static function factory( $which ) { $which = strtolower ( $which ); if ( $which == 'apple' ) { return new Apple(); } elseif ( $which == 'strawberry' ) { return new Strawberry(); } elseif ( $which == 'grape' ) { return new Grape(); } else { throw new BadFruitException( 'Bad fruit request' ); } } } |
可以看出,园丁类提供了一个静态工厂方法。在客户端的调用下,这个方法创建客户端所需要的水果对象。如果客户端的请求是系统所不支持的,工厂方法就会抛出一个BadFruitException 异常。这个异常类的源代码如下所示。
代码清单6:BadFruitException 类的源代码
1
2
3
|
class BadFruitException extends Exception { } |
在使用时,客户端只需调用FruitGardener 的静态方法factory()即可。请见下面的示意
性客户端源代码。
代码清单7:怎样使用异常类BadFruitException
1
2
3
4
5
6
7
8
|
try { FruitGardener::factory( 'apple' ); FruitGardener::factory( 'grape' ); FruitGardener::factory( 'strawberry' ); //... } catch (BadFruitException $e ) { //... } |
这样,农场一定会百果丰收啦!
3.使用简单工厂模式设计一个“面向对象的”计算器
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
128
129
130
131
132
|
/** * 面向对象计算器 * 思路: * 1、面向对象的基本,封装、继承、多太 * 2、父类公用类 * 3、各种运算类 */ /** * 基类,运算类 * 只提供基本数据,不参与运算 */ class Operation { // 第一个数 public $first_num = 0; // 第二个数 public $second_num = 0; /** * 获取结果,其他类覆盖此方法 * @return double $result */ public function getResult() { $result = 0.00; return $result ; } } /** * 加法类 */ class OperationAdd extends Operation { /** * 覆盖父类,实现加法算法 */ public function getResult() { $result = 0; return $this ->first_num + $this ->second_num; } } /** * 减法类 * */ class OperationSub extends Operation { /** * 覆盖父类,实现加法算法 */ public function getResult() { $result = 0; return $this ->first_num - $this ->second_num; } } /** * 乘法类 * */ class OperationMul extends Operation { /** * 覆盖父类,实现加法算法 */ public function getResult() { $result = 0; return $this ->first_num * $this ->second_num; } } /** * 除类 * */ class OperationDiv extends Operation { /** * 覆盖父类,实现加法算法 */ public function getResult() { $result = 0; if ( $this ->second_num == 0) { throw new Exception( '除法操作第二个参数不能为零!' ); return 0; } return $this ->first_num / $this ->second_num; } } /** * 工厂类 */ class OperationFactory { /** * 工厂函数 * @param string $operation * @return object */ public function createOperation( $operation ) { $oper = null; switch ( $operation ) { case '+' : $oper = new OperationAdd(); break ; case '-' : $oper = new OperationSub(); break ; case '*' : $oper = new OperationMul(); break ; case '/' : $oper = new OperationDiv(); break ; default : return 0; } return $oper ; } } $operation = new OperationFactory(); $oper = $operation ->createOperation( '/' ); $oper ->first_num = 10; $oper ->second_num = 20; var_dump( $oper ->getResult()); |