16 个 PHP 设计模式详解(1)
概念
在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。
分类
设计模式主要分为三大类,各自还有许多子类
- 创建型模式
- 结构型模式
- 行为型模式
1.创建型模式
模式名|描述
—|—
抽象工厂模式| 为一个产品族提供了统一的创建接口。当需要这个产品族的某一系列的时候,可以从抽象工厂中选出相应的系列创建一个具体的工厂类
工厂方法模式| 定义一个接口用于创建对象,但是让子类决定初始化哪个类。工厂方法把一个类的初始化下放到子类。
生成器模式|将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
惰性初始模式| 推迟对象的创建、数据的计算等需要耗费较多资源的操作,只有在第一次访问的时候才执行。
对象池模式| 通过回收利用对象避免获取和释放资源所需的昂贵成本。
原型模式| 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
单例模式 |确保一个类只有一个实例,并提供对该实例的全局访问
2.结构性模式
模式名 | 描述 |
---|---|
适配器模式 | 将某个类的接口转换成客户端期望的另一个接口表示。适配器模式可以消除由于接口不匹配所造成的类兼容性问题。 |
桥接模式 | 将一个抽象与实现解耦,以便两者可以独立的变化。 |
组合模式 | 把多个对象组成树状结构来表示局部与整体,这样用户可以一样的对待单个对象和对象的组合。 |
修饰模式 | 向某个对象动态地添加更多的功能。修饰模式是除类继承外另一种扩展功能的方法。 |
外观模式 | 为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 |
享元 | 通过共享以便有效的支持大量小颗粒对象。 |
代理 | 为其他对象提供一个代理以控制对这个对象的访问。 |
3.行为型模式
模式名 | 描述 |
---|---|
黑板 | 广义的观察者在系统范围内交流信息,允许多位读者和写者。 |
责任链 | 为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。 |
命令 | 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。 |
解释器 | 给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。 |
迭代器 | 提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。 |
中介者 | 包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用,从而使它们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用,保证这些作用可以彼此独立的变化。 |
备忘录 | 备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。 |
空对象 | 通过提供默认对象来避免空引用。 |
观察者模式 | 在对象间定义一个一对多的联系性,由此当一个对象改变了状态,所有其他相关的对象会被通知并且自动刷新。 |
规格 | 以布尔形式表示的可重绑定的商业逻辑。 |
状态 | 让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能获取的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。 |
策略 | 定义一个算法的系列,将其各个分装,并且使他们有交互性。策略模式使得算法在用户使用的时候能独立的改变。 |
模板方法 | 模板方法模式准备一个抽象类,将部分逻辑以具体方法及具体构造子类的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先构建一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。 |
访问者 | 封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。 |
从三类设计模式挑选16个常用的设计模式来讲解.
在说模式之前先讲一下UML类图和时序图
UML类图和时序图
- 车的类图结构为<>,表示车是一个抽象类;
- 它有两个继承类:小汽车和自行车;它们之间的关系为实现关系,使用带空心箭头的虚线表示;
- 小汽车为与SUV之间也是继承关系,它们之间的关系为泛化关系,使用带空心箭头的实线表示;
- 小汽车与发动机之间是组合关系,使用带实心菱形的实线表示;
- 学生与班级之间是聚合关系,使用带空心菱形的实线表示;
- 学生与身份证之间为关联关系,使用一根实线表示;
- 学生上学需要用到自行车,与自行车是一种依赖关系,使用带箭头的虚线表示;
类的关系:
- 泛化Generalization
- 实现Realization
- 依赖Dependency
- 关联Association
关联关系又分为:
- 一般关联关系
- 聚合关系Aggregation
- 合成关系Composition
类的继承包括:泛化和实现
泛化关系
泛化关系用一条带空心箭头的直接表示;如下图表示(A继承自B)
例:汽车在现实中有实现,可用汽车定义具体的对象;汽车与SUV之间为泛化关系;
注:最终代码中,泛化关系表现为继承非抽象类;
实现关系
实现关系用一条带空心箭头的虚线表示;
eg:”车”为一个抽象概念,在现实中并无法直接用来定义对象;只有指明具体的子类(汽车还是自行车),才 可以用来定义对象。
注:最终代码中,实现关系表现为继承抽象类;
聚合关系
聚合关系用一条带空心菱形箭头的直线表示,如下图表示A聚合到B上,或者说B由A组成;
!
聚合关系用于表示实体对象之间的关系,表示整体由部分构成的语义;例如一个部门由多个员工组成;
与组合关系不同的是,整体和部分不是强依赖的,即使整体不存在了,部分仍然存在;例如, 部门撤销了,人员不会消失,他们依然存在;
组合关系
组合关系用一条带实心菱形箭头直线表示,如下图表示A组成B,或者B由A组成;
与聚合关系一样,组合关系同样表示整体由部分构成的语义;比如公司由多个部门组成;
但组合关系是一种强依赖的特殊聚合关系,如果整体不存在了,则部分也不存在了;例如, 公司不存在了,部门也将不存在了;
关联关系
关联关系是用一条直线表示的;它描述不同类的对象之间的结构关系;它是一种静态关系, 通常与运行状态无关,一般由常识等因素决定的;它一般用来定义对象之间静态的、天然的结构; 所以,关联关系是一种“强关联”的关系;
比如,乘车人和车票之间就是一种关联关系;学生和学校就是一种关联关系;
关联关系默认不强调方向,表示对象间相互知道;如果特别强调方向,如下图,表示A知道B,但 B不知道A;
注:在最终代码中,关联对象通常是以成员变量的形式实现的;
依赖关系
依赖关系是用一套带箭头的虚线表示的;如下图表示A依赖于B;他描述一个对象在运行期间会用到另一个对象的关系;
与关联关系不同的是,它是一种临时性的关系,通常在运行期间产生,并且随着运行时的变化; 依赖关系也可能发生变化;
显然,依赖也有方向,双向依赖是一种非常糟糕的结构,我们总是应该保持单向依赖,杜绝双向依赖的产生;
注:在最终代码中,依赖关系体现为类构造方法及类方法的传入参数,箭头的指向为调用关系;依赖关系除了临时知道对方外,还是“使用”对方的方法和属性;
时序图
时序图(Sequence DIagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。时序图中显示的是参与交互的对象及其对象之间消息交互的顺序。
时序图包括的建模元素主要有:
- 对象(Actor)
- 生命线(Lifeline)
- 控制焦点(Focus of control)
- 消息(Message)
等等
关于时序图,以下这篇文章将概念介绍的比较详细;更多实例应用,参见后续章节模式中的时序图;
http://smartlife.blog.51cto.com/1146871/284874
设计模式详解
创建型模式
创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。
工厂模式 分为三类
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
简单工厂模式
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
模式结构
角色:
- Factory:
工厂角色
:工厂角色负责实现创建所有实例的内部逻辑 - Product:
抽象产品角色
:抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口 - ConcreteProduct:
具体产品角色
:具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。实现Product父类的接口功能,也可添加自定义的功能
代码实例: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<?php
//简单工厂模式
class Cat
{
function __construct()
{
echo "I am Cat class <br>";
}
}
class Dog
{
function __construct()
{
echo "I am Dog class <br>";
}
}
class Factory
{
public static function CreateAnimal($name){
if ($name == 'cat') {
return new Cat();
} elseif ($name == 'dog') {
return new Dog();
}
}
}
$cat = Factory::CreateAnimal('cat');
$dog = Factory::CreateAnimal('dog');
Factory是工厂角色,里面有CreateAnimal这个抽象产品角色(其实可以有多个抽象产品角色)。
优缺点
简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。
工厂方法模式
此模式中,通过定义一个抽象的核心工厂类,并定义创建产品对象的接口,创建具体产品实例的工作延迟到其工厂子类去完成。这样做的好处是核心类只关注工厂类的接口定义,而具体的产品实例交给具体的工厂子类去创建。当系统需要新增一个产品,无需修改现有系统代码,只需要添加一个具体产品类和其对应的工厂子类,是系统的扩展性变得很好,符合面向对象编程的开闭原则
模式结构
角色:
- Product:抽象产品:
- ConcreteProduct:具体产品
- Factory:抽象工厂
- ConcreteFactory:具体工厂
代码实例: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<?php
//抽象产品
interface Animal{
public function run();
public function say();
}
//具体产品
class Cat implements Animal
{
public function run(){
echo "I ran slowly <br>";
}
public function say(){
echo "I am Cat class <br>";
}
}
//具体产品
class Dog implements Animal
{
public function run(){
echo "I'm running fast <br>";
}
public function say(){
echo "I am Dog class <br>";
}
}
// 抽象工厂
abstract class Factory{
abstract static function createAnimal();
}
//具体工厂
class CatFactory extends Factory
{
public static function createAnimal()
{
return new Cat();
}
}
//具体工厂
class DogFactory extends Factory
{
public static function createAnimal()
{
return new Dog();
}
}
$cat = CatFactory::createAnimal();
$cat->say();
$cat->run();
$dog = DogFactory::createAnimal();
$dog->say();
$dog->run();
优缺点
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
此模式是对工厂方法模式的进一步扩展。在工厂方法模式中,一个具体的工厂负责生产一类具体的产品,即一对一的关系,但是,如果需要一个具体的工厂生产多种产品对象,那么就需要用到抽象工厂模式了。
为了更清晰地理解工厂方法模式,需要先引入两个概念:
- 产品等级结构 :产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
- 产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
模式结构
角色:
抽象工厂(AbstractFactory):担任这个角色的是抽象工厂模式的核心,是与应用系统的商业逻辑无关的。
具体工厂(Factory):这个角色直接在客户端的调用下创建产品的实例,这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统商业逻辑紧密相关的。
抽象产品(AbstractProduct):担任这个角色的类是抽象工厂模式所创建的对象的父类,或它们共同拥有的接口
具体产品(Product):抽象工厂模式所创建的任何产品对象都是一个具体的产品类的实例。
代码实例: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<?php
// 抽象产品,定义产品有哪些功能
interface TV{
public function open();
public function watch();
}
// 抽象产品,定义产品有哪些功能
class HaierTv implements TV
{
public function open()
{
echo "Open Haier TV <br>";
}
public function watch()
{
echo "I'm watching TV <br>";
}
}
// 抽象产品,定义产品有哪些功能
interface PC{
public function work();
public function play();
}
// 具体产品,实现抽象产品定义的功能
class LenovoPc implements PC
{
public function work()
{
echo "I'm working on a Lenovo computer <br>";
}
public function play()
{
echo "Lenovo computers can be used to play games <br>";
}
}
// 抽象工厂,定义创建哪些抽象产品
abstract class Factory{
abstract public static function createPc();
abstract public static function createTv();
}
// 具体工厂,实例化抽象产品
class ProductFactory extends Factory
{
public static function createTV()
{
return new HaierTv();
}
public static function createPc()
{
return new LenovoPc();
}
}
$newTv = ProductFactory::createTV();
$newTv->open();
$newTv->watch();
$newPc = ProductFactory::createPc();
$newPc->work();
$newPc->play();
建造者模式
又名:生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。例如,一辆汽车由轮子,发动机以及其他零件组成,对于普通人而言,我们使用的只是一辆完整的车,这时,我们需要加入一个构造者,让他帮我们把这些组件按序组装成为一辆完整的车。
模式结构
角色:
Builder: 抽象构造者类,为创建一个Product对象的各个部件指定抽象接口。
ConcreteBuilder:具体构造者类,实现Builder的接口以构造和装配该产品的各个部件。定义并明确它所创建的表示。提供一个检索产品的接口
Director:指挥者,构造一个使用Builder接口的对象。
Product:表示被构造的复杂对象。ConcreateBuilder创建该产品的内部表示并定义它的装配过程。
包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
代码实例: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<?php
abstract class Builder
{
protected $car;
abstract public function buildPartA();
abstract public function buildPartB();
abstract public function buildPartC();
abstract public function getResult();
}
class CarBuilder extends Builder
{
function __construct()
{
$this->car = new Car();
}
public function buildPartA(){
$this->car->setPartA('发动机');
}
public function buildPartB(){
$this->car->setPartB('轮子');
}
public function buildPartC(){
$this->car->setPartC('其他零件');
}
public function getResult(){
return $this->car;
}
}
class Car
{
protected $partA;
protected $partB;
protected $partC;
public function setPartA($str){
$this->partA = $str;
}
public function setPartB($str){
$this->partB = $str;
}
public function setPartC($str){
$this->partC = $str;
}
public function show()
{
echo "这辆车由:".$this->partA.','.$this->partB.',和'.$this->partC.'组成';
}
}
class Director
{
public $myBuilder;
public function startBuild()
{
$this->myBuilder->buildPartA();
$this->myBuilder->buildPartB();
$this->myBuilder->buildPartC();
return $this->myBuilder->getResult();
}
public function setBuilder(Builder $builder)
{
$this->myBuilder = $builder;
}
}
$carBuilder = new CarBuilder();
$director = new Director();
$director->setBuilder($carBuilder);
$newCar = $director->startBuild();
$newCar->show();
单例模式
单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。
实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
单例模式的要点有:某个类只能有一个实例;它必须自行创建本身的实例;它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。
角色:
- Singleton:单例类
代码实例: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
32class Singleton
{
private static $instance;
//私有构造方法,禁止使用new创建对象
private function __construct(){}
public static function getInstance(){
if (!isset(self::$instance)) {
self::$instance = new self;
}
return self::$instance;
}
//将克隆方法设为私有,禁止克隆对象
private function __clone(){}
public function say()
{
echo "这是用单例模式创建对象实例 <br>";
}
public function operation()
{
echo "这里可以添加其他方法和操作 <br>";
}
}
// $shiyanlou = new Singleton();
$shiyanlou = Singleton::getInstance();
$shiyanlou->say();
$shiyanlou->operation();
$newShiyanlou = Singleton::getInstance();
var_dump($shiyanlou === $newShiyanlou);
上述的五个模式均属于创建型模式,接下来将要介绍的六个模式属于结构型模式。
结构型模式
结构型模式(Structural Pattern)描述如何将
类
或者对 象
结合在一起形成更大的结构,就像搭积木,可以通过 简单积木的组合形成复杂的、功能更为强大的结构。
结构型模式可以分为类结构型模式
和对象结构型模式
:
- 类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系。
- 对象结构型模式关心
类与对象的组合
,通过关联关系
使得在一 个类中定义另一个类的实例对象,然后通过该对象调用其方法。 根据“合成复用原则”,在系统中尽量使用关联关系来替代继 承关系,因此大部分结构型模式都是对象结构型模式
。适配器模式
在设计模式)中,适配器模式(英语:adapter pattern)有时候也称包装样式或者包装(wrapper)。将一个类)的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
角色:
Target:目标抽象类
Adapter:适配器类
Adaptee:适配者类
Client:客户类
实例代码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
30class Adaptee
{
public function realRequest()
{
echo "这是被适配者真正的调用方法";
}
}
interface Target{
public function request();
}
class Adapter implements Target
{
protected $adaptee;
function __construct(Adaptee $adaptee)
{
$this->adaptee = $adaptee;
}
public function request()
{
echo "适配器转换:";
$this->adaptee->realRequest();
}
}
$adaptee = new Adaptee();
$target = new Adapter($adaptee);
$target->request();
优点:将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
桥接模式
桥接模式是软件设计模式中最复杂的模式之一,它把
事物对象
和其具体行为
、具体特征
分离开来,使它们可以各自独立的变化。事物对象仅是一个抽象的概念。如“圆形”、“三角形”归于抽象的“形状
”之下,而“画圆”、“画三角”归于实现行为的“画图
”类之下,然后由“形状”调用“画图”。
理解桥接模式,重点需要理解如何将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
角色:
Abstraction:定义抽象的接口,该接口包含实现具体行为、具体特征的Implementor接口
Refined Abstraction:抽象接口Abstraction的子类,依旧是一个抽象的事物名
Implementor:定义具体行为、具体特征的应用接口
ConcreteImplementor:实现Implementor接口
实例代码;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<?php
interface DrawingAPI{
public function drawCircle($x,$y,$radius);
}
/**
* drawAPI1
*/
class DrawingAPI1 implements DrawingAPI
{
public function drawCircle($x,$y,$radius)
{
echo "API1.circle at (".$x.','.$y.') radius '.$radius.'<br>';
}
}
/**
* drawAPI2
*/
class DrawingAPI2 implements DrawingAPI
{
public function drawCircle($x,$y,$radius)
{
echo "API2.circle at (".$x.','.$y.') radius '.$radius.'<br>';
}
}
/**
*shape接口
*/
interface Shape{
public function draw();
public function resize($radius);
}
class CircleShape implements Shape
{
private $x;
private $y;
private $radius;
private $drawingAPI;
function __construct($x,$y,$radius,DrawingAPI $drawingAPI)
{
$this->x = $x;
$this->y = $y;
$this->radius = $radius;
$this->drawingAPI = $drawingAPI;
}
public function draw()
{
$this->drawingAPI->drawCircle($this->x,$this->y,$this->radius);
}
public function resize($radius)
{
$this->radius = $radius;
}
}
$shape1 = new CircleShape(1,2,4,new DrawingAPI1());
$shape2 = new CircleShape(1,2,4,new DrawingAPI2());
$shape1->draw();
$shape2->draw();
$shape1->resize(10);
$shape1->draw();
装饰器模式
修饰模式,是面向对象编程领域中,一种动态地往一个类中添加
新的行为
的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能
。
一般来说,给一个对象或者类增加行为的方式可以有两种:
- 继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。
- 关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)
通过使用修饰模式,可以在运行时扩充一个类的功能。原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。
修饰模式是类继承的另外一种选择。类继承在编译时候增加行为,而装饰模式是在运行时增加行为。
角色
Component: 抽象构件
ConcreteComponent: 具体构件
Decorator: 抽象装饰类
ConcreteDecorator: 具体装饰类
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
<?php
abstract class Component {
abstract public function operation();
}
class MyComponent extends Component
{
public function operation()
{
echo "这是正常的组件方法 <br>";
}
}
abstract class Decorator extends Component {
protected $component;
function __construct(Component $component)
{
$this->component = $component;
}
public function operation()
{
$this->component->operation();
}
}
class MyDecorator extends Decorator
{
function __construct(Component $component)
{
parent::__construct($component);
}
public function addMethod()
{
echo "这是装饰器添加的方法 <br>";
}
public function operation()
{
$this->addMethod();
parent::operation();
}
}
$component = new MyComponent();
$da = new MyDecorator($component);
$da->operation();
外观模式
外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式
举一个简单的例子,相信大家都使用过 C++ 语言,他是一门编译型语言,写完代码之后,我们需要经过编译之后才能运行,在IDE中,会有一个 Build 的按钮,点击它即可完成编译过程,但是这一个简单的动作背后,却是一系列复杂操作的协调配合,至少包括词法分析,语法分析,生成中间代码,生成汇编代码以及链接等操作,作为普通开发人员,我们不必在意这些过程是如何完成的,只需要点击Build按钮,IDE就会自动帮我们完成背后的工作。那么这个Build按钮就是IDE为我们提供的高级接口,通过他来完成各种子系统的协调工作。
角色:
- Facade:外观角色,提供高级接口
- SubSystem:子系统角色,负责各自的功能实现
实例代码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<?php
class SystemA
{
public function operationA()
{
echo "operationA <br>";
}
}
class SystemB
{
public function operationB()
{
echo "operationB <br>";
}
}
class SystemC
{
public function operationC()
{
echo "operationC <br>";
}
}
class Facade
{
protected $systemA;
protected $systemB;
protected $systemC;
function __construct()
{
$this->systemA = new SystemA();
$this->systemB = new SystemB();
$this->systemC = new SystemC();
}
public function myOperation()
{
$this->systemA->operationA();
$this->systemB->operationB();
$this->systemC->operationC();
}
}
$facade = new Facade();
$facade->myOperation();
优缺点:
使用外观模式最大的优点就是子系统与客户端之间是松耦合的关系,客户端不必知道具体有哪些子系统,也无需知道他们是如何工作的,通过引入一个外观类,提供一个客户端间接访问子系统的高级接口。子系统和外观类可以独立运作,修改某一个子系统的内容,不会影响到其他子系统,也不会影响到外观对象。不过它的缺点就是它不够灵活,当需要增加一个子系统的时候,需要修改外观类。
享元模式
享元模式(英语:Flyweight Pattern)是一种软件设计模式)。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
要理解享元模式,先要理解两个重要的概念:内部状态和外部状态。
内部状态存储于flyweight中,它包含了独立于flyweight场景的信息,这些信息使得flyweight可以被共享。而外部状态取决于flyweight场景,并根据场景而变化,因此不可共享。用户对象负责在必要的时候将外部状态传递给flyweight。
角色
- Flyweight: 抽象享元类
- ConcreteFlyweight: 具体享元类
- UnsharedConcreteFlyweight: 非共享具体享元类
- FlyweightFactory: 享元工厂类
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<?php
interface Flyweight{
public function operation();
}
class MyFlyweight implements Flyweight
{
protected $intrinsicState;
function __construct($str)
{
$this->intrinsicState = $str;
}
public function operation()
{
echo 'MyFlyweight['.$this->intrinsicState.'] do operation. <br>';
}
}
class FlyweightFactory
{
protected static $flyweightPool;
function __construct()
{
if (!isset(self::$flyweightPool)) {
self::$flyweightPool = [];
}
}
public function getFlyweight($str)
{
if (!array_key_exists($str,self::$flyweightPool)) {
$fw = new MyFlyweight($str);
self::$flyweightPool[$str] = $fw;
return $fw;
} else {
echo "aready in the pool,use the exist one: <br>";
return self::$flyweightPool[$str];
}
}
}
$factory = new FlyweightFactory();
$fw = $factory->getFlyweight('one');
$fw->operation();
$fw1 = $factory->getFlyweight('two');
$fw1->operation();
$fw2 = $factory->getFlyweight('one');
$fw2->operation();
享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
代理模式
所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网络连接、内存中的大对象、文件或其它昂贵或无法复制的资源。
代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。
可能大家听得最多且最常用的就是VPN网络代理,或者代理服务器等。
角色
-Subject: 抽象主题角色
- Proxy: 代理主题角色
- RealSubject: 真实主题角色
实例代码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 Subject{
public function request();
}
class RealSubject implements Subject
{
public function request()
{
echo "RealSubject::request <br>";
}
}
class Proxy implements Subject
{
protected $realSubject;
function __construct()
{
$this->realSubject = new RealSubject();
}
public function beforeRequest()
{
echo "Proxy::beforeRequest <br>";
}
public function request()
{
$this->beforeRequest();
$this->realSubject->request();
$this->afterRequest();
}
public function afterRequest()
{
echo "Proxy::afterRequest <br>";
}
}
$proxy = new Proxy();
$proxy->request();
下面将会介绍五种行为型模式。
行为型模式
行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。
行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。
通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象 之间的交互。在系统运行时,对象并不是孤立的,它们可以通过相互通信与协作完成某些复杂功能,一个对象在运行时也将影响到其他对象的运行。
行为型模式分为类行为型模式和对象行为型模式两种:
- 类行为型模式:类的行为型模式使用继承关系在几个类之间分配行为,类行为型模式主要通过多态等方式来分配父类与子类的职责。
- 对象行为型模式:对象的行为型模式则使用对象的聚合关联关系来分配行为,对象行为型模式主要是通过对象关联等方式来分配两个或多个类的职责。根据“合成复用原则”,系统中要尽量使用关联关系来取代继承关系,因此大部分行为型设计模式都属于对象行为型设计模式
命令模式
在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。
主要特点就是将一个请求封装为一个对象
,从而使我们可用不同的请求对客户进行参数化
;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式`
或事务(Transaction)模式`
角色
- Command: 抽象命令类
- ConcreteCommand: 具体命令类
- Invoker: 调用者
- Receiver: 接收者
- Client:客户类
示例代码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<?php
class Receiver
{
public function Action()
{
echo "Receiver->Action";
}
}
abstract class Command{
protected $receiver;
function __construct(Receiver $receiver)
{
$this->receiver = $receiver;
}
abstract public function Execute();
}
class MyCommand extends Command
{
function __construct(Receiver $receiver)
{
parent::__construct($receiver);
}
public function Execute()
{
$this->receiver->Action();
}
}
class Invoker
{
protected $command;
function __construct(Command $command)
{
$this->command = $command;
}
public function Invoke()
{
$this->command->Execute();
}
}
$receiver = new Receiver();
$command = new MyCommand($receiver);
$invoker = new Invoker($command);
$invoker->Invoke();
中介者模式
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
举个简单的例子,就比如大家平时喜欢用微信聊天,你发送的聊天内容需要通过微信服务器进行中间处理,然后下发给你的好友,微信服务器就是一个中介者。
角色
- Mediator: 抽象中介者
- ConcreteMediator: 具体中介者
- Colleague: 抽象同事类
- ConcreteColleague: 具体同事类
1 | <?php |
中介者模式的两个主要作用:中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,通过中介者即可。该中转作用属于中介者在结构上的支持。
协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致地和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。
观察者模式
在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来
实时事件处理系统
。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
角色
Subject: 抽象目标类,一般至少提供三个接口:
- 添附(Attach):新增观察者到串炼内,以追踪目标对象的变化。
- 解附(Detach):将已经存在的观察者从串炼中移除。
- 通知(Notify):利用观察者所提供的更新函式来通知此目标已经产生变化。
ConcreteSubject: 具体目标,提供了观察者欲追踪的状态,也可设置目标状态
Observer: 抽象观察者,定义观察者的更新操作接口
ConcreteObserver: 具体观察者,实现抽象观察者的接口,做出自己的更新操作
生活实例:
举一个生活中的例子:比如用户(观察者)从报社订阅报纸,报社和用户之间是一对多依赖,用户可以在报社订阅(register)报纸,报社可以把最新的报纸发给用户(notify),用户自动收到更新。在用户不需要的时候还可以取消注册(remove)。
示例代码
1 | <?php |
状态模式
状态模式:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。
有时,一个对象的行为受其一个或多个具体的属性变化而变化,这样的属性也叫作状态,这样的的对象也叫作有状态的对象。
角色
Context: 环境类,维护一个ConcreteState子类的实例,这个实例定义当前状态;
State: 抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为;
ConcreteState: 具体状态类,每一个子类实现一个与Context的一个状态相关的行为。
1 | <?php |
策略模式
策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。
常见示例:常见的排序算法有快速排序,冒泡排序,归并排序,选择排序等,如果我们需要在一个算法类中提供这些算法,一个常见的解决方法就是在类中定义多个方法,每个方法定义一种具体的排序算法,然后使用 if…else…去判断到底是哪种算法,或者直接调用某个具体方法。这种方法是将算法的实现硬编码到类中,这样做最大的弊端就是算法类类非常臃肿,而且当需要增加或者更换一种新的排序方法时候,需要修改算法类的代码,同时也需要修改客户端调用处的代码。策略模式就是为了解决这列问题而设计的。
角色
Context: 环境类,使用一个ConcreteStrategy对象来配置;维护一个对Stategy对象的引用,同时,可以定义一个接口来让Stategy访问它的数据。
Strategy: 抽象策略类,定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法;
ConcreteStrategy: 具体策略类,实现 Strategy 接口的具体算法;
1 | <?php |
【参考来源】
本作品采用 知识共享署名 2.5 中国大陆许可协议 进行许可,欢迎转载,但转载请注明来自JayMo,并保持转载后文章内容的完整。本人保留所有版权相关权利。
本文永久链接:http://jaymo666.github.io/2018/04/16/设计模式详解/