设计模式
并非所有开发语言都支持所有设计模式,有的开发语言只支持个别设计模式。
创建型模式(Creational)
关注对象的实例化过程,包括了如何实例化对象、隐藏对象的创建细节等。
单例模式(Singleton Pattern)
确保一个类只有一个实例,并提供该实例的全局访问点。
数据库类设计,只连接一次数据库,防止打开多个数据库连接
使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。
私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class Single {
private $name;//声明一个私有的实例变量
private function __construct(){//声明私有构造方法为了防止外部代码使用new来创建对象。
}
static public $instance;//声明一个静态变量(保存在类中唯一的一个实例)
static public function getinstance(){//声明一个getinstance()静态方法,用于检测是否有实例对象
if(!self::$instance) self::$instance = new self();
return self::$instance;
}
public function setname($n){ $this->name = $n; }
public function getname(){ return $this->name; }
}
$oa = Single::getinstance();
$ob = Single::getinstance();
$oa->setname('hello php world');
$ob->setname('good morning php');
echo $oa->getname();//good morning php
echo $ob->getname();//good morning php
|
简单工厂
在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。
简单工厂又叫静态工厂方法模式,简单工厂模式是通过一个静态方法创建对象的。
违反开闭原则(对于扩展是开放的,对于修改是关闭的)
新增时需要改动原有类文件,在里面新增方法或判断。
调用时用同一个对象,但用的方法不一样,或传进去的参数不一样。如:
| <?php
SimpleFactoty::creatA();
SimpleFactoty::creatB();
SimpleFactoty::creatC();
// or
SimpleFactoty::creat("A");
SimpleFactoty::creat("B");
SimpleFactoty::creat("C");
|
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 | <?php
interface people
{
function marry();
}
class man implements people
{
function marry()
{
echo '送玫瑰,送戒指!';
}
}
class women implements people
{
function marry()
{
echo '穿婚纱!';
}
}
class SimpleFactoty
{
// 简单工厂里的静态方法
static function createMan()
{
return new man;
}
static function createWomen()
{
return new women;
}
}
SimpleFactoty::createMan()->marry();
// 送玫瑰,送戒指!
SimpleFactoty::createWomen()->marry();
// 穿婚纱!
|
简单工厂: 调用时SimpleFactoty一样,createMan方法不一样
工厂方法: 调用时SimpleFactoty不一样,createMan方法一样
工厂方法
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
遵循开闭原则。
新增方法不用改原有类文件,新增文件后继承上游类或接口即可调用。
调用时,对象不一样,但调用方法和返回类型皆为一致。
如:
| <?php
FactoryA::create();
FactoryB::create();
FactoryC::create();
?>
|
使用场景:
- 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
- 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
- 设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口。
在工厂方法中,由子类来创建对象。
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 | <?php
interface people
{
function marry();
}
class man implements people
{
function marry()
{
echo '送玫瑰,送戒指!';
}
}
class women implements people
{
function marry()
{
echo '穿婚纱!';
}
}
interface createMan
{ // 注意了,这里是简单工厂本质区别所在,将对象的创建抽象成一个接口。
function create();
}
class FactoryMan implements createMan
{
function create()
{
return new man;
}
}
class FactoryWomen implements createMan
{
function create()
{
return new women;
}
}
FactoryMan::create()->marry();
//送玫瑰,送戒指!
FactoryWomen::create()->marry();
//穿婚纱!
?>
|
与简单工厂不同的是前面的FactoryMan对象是变化的,create方法是固定的。
抽象工厂
提供一个接口,用于创建 相关的对象家族 。
抽象工厂模式是工厂模式的一个扩展,如果抽象工厂只有一个产品体系就会退化成工厂模式。属于简单工厂和工厂模式的合体。
违反开闭原则
新增时 可能 需要改动原有类文件,在里面新增方法或判断。同样要继承上游类或接口。
调用时,对象和调用方法都可不一样。
如:
| <?php
FactoryA::create();
FactoryB::change();
FactoryC::change();
FactoryD::delete();
?>
|
体现出了面向接口编程的思想,其实就是用工厂方法生产具有多维度变化的产品类。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 工厂里加代码,又要在具体的实现加代码
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 | <?php
interface people
{
function marry();
}
class Oman implements people
{
function marry()
{
echo '美女,我送你玫瑰和戒指!';
}
}
class Iman implements people
{
function marry()
{
echo '我偷偷喜欢你';
}
}
class Owomen implements people
{
function marry()
{
echo '我要穿婚纱!';
}
}
class Iwomen implements people
{
function marry()
{
echo '我好害羞哦!!';
}
}
interface createMan
{ // 注意了,这里是本质区别所在,将对象的创建抽象成一个接口。
function createOpen(); //分为 内敛的和外向的
function createIntro(); //内向
}
class FactoryMan implements createMan
{
function createOpen()
{
return new Oman;
}
function createIntro()
{
return new Iman;
}
}
class FactoryWomen implements createMan
{
function createOpen()
{
return new Owomen;
}
function createIntro()
{
return new Iwomen;
}
}
FactoryMan::createOpen()->marry(); //美女,我送你玫瑰和戒指!
FactoryMan::createIntro()->marry(); //我偷偷喜欢你
FactoryWomen::createOpen()->marry(); //我要穿婚纱!
FactoryWomen::createIntro()->marry(); //我好害羞哦!!
?>
|
生成器
封装一个对象的构造过程,并允许按步骤构造。
原型模式
使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。
行为型
责任链
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。
命令
将命令封装成对象中,具有以下作用:
- 使用命令来参数化其它对象
- 将命令放入队列中进行排队
- 将命令的操作记录到日志中
- 支持可撤销的操作
解释器
为语言创建解释器,通常由语言的语法和语法分析来定义。
迭代器
提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。
中介者
集中相关对象之间复杂的沟通和控制方式。
备忘录
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。
观察者
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
状态
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
策略
定义一系列算法,封装每个算法,并使它们可以互换。
策略模式可以让算法独立于使用它的客户端。
模板方法
定义算法框架,并将一些步骤的实现延迟到子类。
通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
访问者
为一个对象结构(比如组合结构)增加新能力。
空对象
使用什么都不做
的空对象来代替 NULL。
一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。
结构型
适配器
把一个类接口转换成另一个用户需要的接口。
桥接
将抽象与实现分离开来,使它们可以独立变化。
组合
将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。
装饰
为对象动态添加功能。
外观
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
享元
利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。
代理
控制对其它对象的访问。