##一、创建型设计模式
####抽象工厂设计模式(abstract_factory)
抽象工厂模式(Abstact Factory)是一种常见的软件设计模式。该模式为一个产品族提供了统一的创建接口。当需要这个产品族的某一系列的时候,可以为此系列的产品族创建一个 具体的工厂类。
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式
- 优点:
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
- 缺点:
- 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
- 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
<?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();
####简单工厂模式(simple_factory)
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式。
在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
- 优点:
- 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
- 缺点
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
//简单工厂模式
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_method)
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式
在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
- 优点:
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
- 缺点:
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
<?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();
####原型设计模式(prototype)
原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
- 优点
- 如果创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
- 可以使用深克隆保持对象的状态
- 原型模式提供了简化的创建结构
- 缺点
- 在实现深克隆的时候可能需要比较复杂的代码
- 需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事,必须修改其源代码,违背了“开闭原则”
Prototype.php
<?php
/**抽象原型类
* Class Prototype
*/
abstract class Prototype
{
abstract function cloned();
}
/**具体原型类
* Class Plane
*/
class Plane extends Prototype
{
public $color;
function Fly()
{
echo "飞机飞啊飞!<br/>";
}
function cloned()
{
return clone $this;
}
}
client.php
<?php
header("Content-Type:text/html;charset=utf-8");
//------------------------原型模式测试代码------------------
require_once "./Prototype/Prototype.php";
$plane1=new Plane();
$plane1->color="Blue";
$plane2=$plane1->cloned();
$plane1->Fly();
$plane2->Fly();
echo "plane1的颜色为:{$plane1->color}<br/>";
echo "plane2的颜色为:{$plane2->color}<br/>";
####单例设计模式(singleton)
单例模式(Singleton Pattern),也叫单子模式,又名单件模式或单态模式
实现单例模式的思路是:
一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);
当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;
同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
- 优点:
- 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
- 缺点:
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,
<?php
class 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);
####构造者设计模式(builder)
构造者模式(Builder Pattern),又可以称为生成器模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
构造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节
- 优点:
- 在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
- 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
- 缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
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();
##二、结构型设计模式
####适配器设计模式(adapter)
适配器模式(Adapter Pattern) ,有时候也称包装样式或者包装。
将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
- 优点:
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
- 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
- 对类适配器来说:由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
- 对对象适配器来说:一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口
- 缺点:
- 类适配器:对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口
- 对象适配器:与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂
<?php
class 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();
####装饰器设计模式(decorator)
其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”
动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活
- 优点:
- 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
- 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”
- 缺点:
- 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
- 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐
<?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();
####代理模式(proxy)
给某一个对象提供一个代 理,并由代理对象控制对原对象的引用
- 优点:
- 代理模式能够协调调用者和被调用者,在一定程度上降低了系 统的耦合度。
- 远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
- 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
- 缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此 有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现 非常复杂。
<?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();
####桥接模式(bridge)
桥接模式(bridge),又称为柄体(Handle and Body)模式或接口(Interface)模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化
- 优点:
- 分离抽象接口及其实现部分。桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
- 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
- 实现细节对客户透明,可以对用户隐藏实现细节。
- 缺点:
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进;
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
<?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();
####组合模式(composite)
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
- 优点
- 组合模式使得客户端代码可以一致地处理对象和对象容器,无需关系处理的单个对象,还是组合的对象容器
- 将”客户代码与复杂的对象容器结构“解耦
- 可以更容易地往组合对象中加入新的构件
- 缺点
- 使得设计更加复杂。
- 客户端需要花更多时间理清类之间的层次关系。(这个是几乎所有设计模式所面临的问题)
- 注意
- 有时候系统需要遍历一个树枝结构的子构件很多次,这时候可以考虑把遍历子构件的结构存储在父构件里面作为缓存
- 客户端尽量不要直接调用树叶类中的方法(在我上面实现就是这样的,创建的是一个树枝的具体对象;),而是借用其父类(Graphics)的多态性完成调用,这样可以增加代码的复用性
<?php
/**
* 组合模式抽象基类
*/
abstract class CompanyBase{
//节点名称
protected $name;
public function __construct($name){
$this->name = $name;
}
public function getName(){
return $this->name;
}
//增加节点
abstract function add(CompanyBase $c);
//删除节点
abstract function remove(CompanyBase $c);
//输出节点信息
abstract function show($deep);
//节点职责
abstract function work($deep);
}
/**
* 公司类
*/
class Company extends CompanyBase{
protected $item = [];
public function add(CompanyBase $c){
$nodeName = $c->getName();
if(!isset( $this->item[$nodeName] )){
$this->item[$nodeName] = $c;
}else{
throw new Exception("该节点已存在,节点名称:".$nodeName);
}
}
public function remove(CompanyBase $c){
$nodeName = $c->getName();
if(isset( $this->item[$nodeName] )){
unset($this->item[$nodeName]);
}else{
throw new Exception("该节点不存在,节点名称:".$nodeName);
}
}
public function show($deep = 0){
echo str_repeat("-",$deep).$this->name;
echo "<br>";
foreach($this->item as $value){
$value->show($deep+4);
}
}
public function work($deep = 0){
foreach($this->item as $value){
echo str_repeat(" ",$deep)."[{$this->name}]<br>";
$value->work($deep+2);
}
}
}
/**
* 人力资源部门
*/
class HumanResources extends CompanyBase{
public function add(CompanyBase $c){
throw new Exception("该节点下不能增加节点");
}
public function remove(CompanyBase $c){
throw new Exception("该节点下无子节点");
}
public function show($deep = 0){
echo str_repeat("-",$deep).$this->name;
echo "<br>";
}
public function work($deep = 0){
echo str_repeat(" ",$deep)."人力资源部门的工作是为公司招聘人才";
echo "<br>";
}
}
/**
* 商务部门
*/
class Commerce extends CompanyBase{
public function add(CompanyBase $c){
throw new Exception("该节点下不能增加节点");
}
public function remove(CompanyBase $c){
throw new Exception("该节点下无子节点");
}
public function show($deep = 0){
echo str_repeat("-",$deep).$this->name;
echo "<br>";
}
public function work($deep = 0){
echo str_repeat(" ",$deep)."商务部门的工作是为公司赚取利润";
echo "<br>";
}
}
/****************************************************************************************/
$c = new Company("北京某科技公司");
$h = new HumanResources("人力资源部门");
$com = new Commerce("商务部门");
$c->add($h);
$c->add($com);
//天津分公司
//为了偷懒,分公司的部门直接copy母公司的
$c1 = new Company("天津分公司");
$c1->add($h);
$c1->add($com);
$c->add($c1);
//武汉分公司
$c2 = new Company("武汉分公司");
$c2->add($h);
$c2->add($com);
$c->add($c2);
//使用公司功能
$c->show();
$c->work();
####外观模式(facade)
外观模式(facade),又称为门面模式
外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
- 优点:
- 对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入外观模式,客户代码将变得很简单,与之关联的对象也很少。
- 实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。
- 降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
- 缺点:
- 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
- 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
<?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)
享元模式(flyweight),又称为轻量级模式
运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用
- 优点:
- 享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
- 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享
- 缺点:
- 享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
- 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长
<?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();
##三、行为型设计模式
####中介者模式(mediator)
又称为调停者模式
用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
- 优点:
- 简化了对象之间的交互。
- 将各同事解耦。
- 减少子类生成。
- 可以简化各同事类的设计和实现。
- 缺点:
- 在具体中介者类中包含了同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护
<?php
abstract class Colleague{
protected $mediator;
abstract public function sendMsg($who,$msg);
abstract public function receiveMsg($msg);
public function setMediator(Mediator $mediator){
$this->mediator = $mediator;
}
}
class ColleagueA extends Colleague
{
public function sendMsg($toWho,$msg)
{
echo "Send Msg From ColleagueA To: ".$toWho . '<br>';
$this->mediator->opreation($toWho,$msg);
}
public function receiveMsg($msg)
{
echo "ColleagueA Receive Msg: ".$msg . '<br>';
}
}
class ColleagueB extends Colleague
{
public function sendMsg($toWho,$msg)
{
echo "Send Msg From ColleagueB To: ".$toWho . '<br>';
$this->mediator->opreation($toWho,$msg);
}
public function receiveMsg($msg)
{
echo "ColleagueB Receive Msg: ".$msg . '<br>';
}
}
abstract class Mediator{
abstract public function opreation($id,$message);
abstract public function register($id,Colleague $colleague);
}
class MyMediator extends Mediator
{
protected static $colleagues;
function __construct()
{
if (!isset(self::$colleagues)) {
self::$colleagues = [];
}
}
public function opreation($id,$message)
{
if (!array_key_exists($id,self::$colleagues)) {
echo "colleague not found";
return;
}
$colleague = self::$colleagues[$id];
$colleague->receiveMsg($message);
}
public function register($id,Colleague $colleague)
{
if (!in_array($colleague, self::$colleagues)) {
self::$colleagues[$id] = $colleague;
}
$colleague->setMediator($this);
}
}
$colleagueA = new ColleagueA();
$colleagueB = new ColleagueB();
$mediator = new MyMediator();
$mediator->register(1,$colleagueA);
$mediator->register(2,$colleagueB);
$colleagueA->sendMsg(2,'hello admin');
$colleagueB->sendMsg(1,'shiyanlou');
####状态设计模式(state)
其别名为状态对象(Objects for States)
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类
- 优点:
- 封装了转换规则。
- 枚举可能的状态,在枚举状态之前需要确定状态种类。
- 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
- 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
- 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
- 缺点:
- 状态模式的使用必然会增加系统类和对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
- 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的 状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
<?php
class Context{
protected $state;
function __construct()
{
$this->state = StateA::getInstance();
}
public function changeState(State $state)
{
$this->state = $state;
}
public function request()
{
$this->state->handle($this);
}
}
abstract class State{
abstract function handle(Context $context);
}
class StateA extends State
{
private static $instance;
private function __construct(){}
private function __clone(){}
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new self;
}
return self::$instance;
}
public function handle(Context $context)
{
echo "doing something in State A.\n done,change state to B <br>";
$context->changeState(StateB::getInstance());
}
}
class StateB extends State
{
private static $instance;
private function __construct(){}
private function __clone(){}
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new self;
}
return self::$instance;
}
public function handle(Context $context)
{
echo "doing something in State B.\n done,change state to A <br>";
$context->changeState(StateA::getInstance());
}
}
$context = new Context();
$context->request();
$context->request();
$context->request();
$context->request();
####责任链模式(responsibility_chain)
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。
这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式
- 优点
- 降低耦合度。它将请求的发送者和接收者解耦
- 简化了对象。使得对象不需要知道链的结构
- 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任
- 增加新的请求处理类很方便
- 缺点
- 不能保证请求一定被接收
- 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用
- 可能不容易观察运行时的特征,有碍于除错
<?php
/**
* 职责链模式
*/
//抽象处理者角色(Handler:Approver)
abstract class Handler
{
public function SetSuccessor(Handler $successor){
$this->successor=$successor;
}
abstract public function HandleRequest($request);
}
//具体处理者角色(ConcreteHandler:President)
class ConcreteHandle1 extends Handler
{
public function HandleRequest($request)
{
if($request>=0&&$request<10){
var_dump(self::class.':'.$request);
}elseif ($this->successor!=null){
$this->successor->HandleRequest($request);
}
}
}
class ConcreteHandle2 extends Handler
{
public function HandleRequest($request)
{
if($request>=10&&$request<20){
var_dump(self::class.':'.$request);
}elseif ($this->successor!=null){
$this->successor->HandleRequest($request);
}
}
}
class ConcreteHandle3 extends Handler
{
public function HandleRequest($request)
{
if($request>=20&&$request<30){
var_dump(self::class.':'.$request);
}elseif ($this->successor!=null){
$this->successor->HandleRequest($request);
}
}
}
$h1=new ConcreteHandle1();
$h2=new ConcreteHandle2();
$h3=new ConcreteHandle3();
$h1->SetSuccessor($h2);
$h2->SetSuccessor($h3);
$h1->HandleRequest(5);
$h1->HandleRequest(15);
$h1->HandleRequest(25);
####观察者模式(observer)
观察者模式又叫做发布-订阅(Publish/Subscribe)模式、
模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式
定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新
- 优点:
- 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
- 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
- 观察者模式支持广播通信。
- 观察者模式符合“开闭原则”的要求。
- 缺点:
- 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
<?php
abstract class Obeserver{
abstract function update(Subject $sub);
}
abstract class Subject{
protected static $obeservers;
function __construct()
{
if (!isset(self::$obeservers)) {
self::$obeservers = [];
}
}
public function attach(Obeserver $obeserver){
if (!in_array($obeserver, self::$obeservers)) {
self::$obeservers[] = $obeserver;
}
}
public function deattach(Obeserver $obeserver){
if (in_array($obeserver, self::$obeservers)) {
$key = array_search($obeserver,self::$obeservers);
unset(self::$obeservers[$key]);
}
}
abstract public function setState($state);
abstract public function getState();
public function notify()
{
foreach (self::$obeservers as $key => $value) {
$value->update($this);
}
}
}
class MySubject extends Subject
{
protected $state;
public function setState($state)
{
$this->state = $state;
}
public function getState()
{
return $this->state;
}
}
class MyObeserver extends Obeserver
{
protected $obeserverName;
function __construct($name)
{
$this->obeserverName = $name;
}
public function update(Subject $sub)
{
$state = $sub->getState();
echo "Update Obeserver[".$this->obeserverName.'] State: '.$state . '<br>';
}
}
$subject = new MySubject();
$one = new MyObeserver('one');
$two = new MyObeserver('two');
$subject->attach($one);
$subject->attach($two);
$subject->setState(1);
$subject->notify();
echo "--------------------- <br>";
$subject->setState(2);
$subject->deattach($two);
$subject->notify();
####策略模式(strategy)
策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)
策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。
- 优点:
- 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
- 策略模式提供了管理相关的算法族的办法。
- 策略模式提供了可以替换继承关系的办法。
- 使用策略模式可以避免使用多重条件转移语句。
- 缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
- 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。
<?php
abstract class Strategy{
abstract function use();
}
class StrategyA extends Strategy
{
public function use()
{
echo "这是使用策略A的方法 <br>";
}
}
class StrategyB extends Strategy
{
public function use()
{
echo "这是使用策略B的方法 <br>";
}
}
class Context
{
protected $startegy;
public function setStrategy(Strategy $startegy)
{
$this->startegy = $startegy;
}
public function use()
{
$this->startegy->use();
}
}
$context = new Context();
$startegyA = new StrategyA();
$startegyB = new StrategyB();
$context->setStrategy($startegyA);
$context->use();
$context->setStrategy($startegyB);
$context->use();
####命令模式(command)
别名为动作(Action)模式或事务(Transaction)模式
将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作
- 优点:
- 降低系统的耦合度。
- 新的命令可以很容易地加入到系统中。
- 可以比较容易地设计一个命令队列和宏命令(组合命令)。
- 可以方便地实现对请求的Undo和Redo。
- 缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类。
- 因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
<?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();
解释器模式(interpreter)
解释器模式 用于分析一个实体的关键元素,并且针对每个元素提供自己的解释或相应动作。解释器模式非常常用,比如PHP的模板引擎 就是非常常见的一种解释器模。
- 优点
- 可扩展性比较好,灵活
- 增加了新的解释表达式的方式
- 易于实现简单文法
- 缺点
- 可利用场景比较少
- 对于复杂的文法比较难维护
- 解释器模式会引起类膨胀
- 解释器模式采用递归调用方法
代码一:解释器模式
<?php
class User
{
protected $_username;
public function __construct($username)
{
$this->_username = $username;
}
public function getProfilePage()
{
$profile = "<h2>I like never again</h2>";
$profile .= "I love all of the songs, My favorite CD:<br/>";
$profile .= "{{myCD.getTitle}}<br/>";
$profile .= "My name is ";
$profile .= "{{myCD.getName}}";
return $profile;
}
}
/**
*ad
*/
class userCD
{
protected $_user = NULL;
public function setUser($user)
{
$this->_user = $user;
}
public function getTitle()
{
return 'test';
}
public function getName()
{
return '111';
}
}
/**
* interpreter
*/
class userCDInterpreter
{
protected $_user = NULL;
function setUser($user)
{
$this->_user = $user;
}
public function getInterpreted()
{
$profile = $this->_user->getProfilePage();
if (preg_match_all('/\{\{myCD\.(.*?)\}\}/', $profile, $triggers, PREG_SET_ORDER)) {
$replacements = array();
foreach ($triggers as $trigger) {
$replacements[] = $trigger[1];
}
$replacements = array_unique($replacements);
$myCD = new userCD();
$myCD->setUser($this->_user);
foreach ($replacements as $replacement) {
$profile = str_replace("{{myCD.{$replacement}}}", call_user_func(array($myCD, $replacement)), $profile);
}
}
return $profile;
}
}
$username = 'aaron';
$user = new User($username);
$interpreter = new userCDInterpreter();
$interpreter->setUser($user);
$echo = $interpreter->getInterpreted();
print "<h1>{$username}'s Profile</h1>";
print $echo;
代码二:解释器模式
<?php
header("Content-type:text/html;Charset=utf-8");
//环境角色,定义要解释的全局内容
class Expression{
public $content;
function getContent(){
return $this->content;
}
}
//抽象解释器
abstract class AbstractInterpreter{
abstract function interpret($content);
}
//具体解释器,实现抽象解释器的抽象方法
class ChineseInterpreter extends AbstractInterpreter{
function interpret($content){
for($i=1;$i<count($content);$i++){
switch($content[$i]){
case '0': echo "没有人<br>";break;
case "1": echo "一个人<br>";break;
case "2": echo "二个人<br>";break;
case "3": echo "三个人<br>";break;
case "4": echo "四个人<br>";break;
case "5": echo "五个人<br>";break;
case "6": echo "六个人<br>";break;
case "7": echo "七个人<br>";break;
case "8": echo "八个人<br>";break;
case "9": echo "九个人<br>";break;
default:echo "其他";
}
}
}
}
class EnglishInterpreter extends AbstractInterpreter{
function interpret($content){
for($i=1;$i<count($content);$i++){
switch($content[$i]){
case '0': echo "This is nobody<br>";break;
case "1": echo "This is one people<br>";break;
case "2": echo "This is two people<br>";break;
case "3": echo "This is three people<br>";break;
case "4": echo "This is four people<br>";break;
case "5": echo "This is five people<br>";break;
case "6": echo "This is six people<br>";break;
case "7": echo "This is seven people<br>";break;
case "8": echo "This is eight people<br>";break;
case "9": echo "This is nine people<br>";break;
default:echo "others";
}
}
}
}
//封装好的对具体解释器的调用类,非解释器模式必须的角色
class Interpreter{
private $interpreter;
private $content;
function __construct($expression){
$this->content = $expression->getContent();
if($this->content[0] == "Chinese"){
$this->interpreter = new ChineseInterpreter();
}else{
$this->interpreter = new EnglishInterpreter();
}
}
function execute(){
$this->interpreter->interpret($this->content);
}
}
//测试
$expression = new Expression();
$expression->content = array("Chinese",3,2,4,4,5);
$interpreter = new Interpreter($expression);
$interpreter->execute();
$expression = new Expression();
$expression->content = array("English",1,2,3,0,0);
$interpreter = new Interpreter($expression);
$interpreter->execute();
//结果:
三个人
二个人
四个人
四个人
五个人
This is one people
This is two people
This is three people
This is nobody
This is nobody
?>
代码三:解释器模式
迭代器模式(decorator)
迭代器模式是遍历集合的成熟模式,迭代器模式的关键是将遍历集合的任务交给一个叫做迭代器的对象,它的工作时遍历并选择序列中的对象,而客户端程序员不必知道或关心该集合序列底层的结构。
- 优点
- 分离了集合对象的遍历行为
- 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心
- 缺点
- 类的个数成对增加
代码一:迭代器模式
<?php
//book.php
namespace DesignPatterns\Behavioral\Iterator;
class Book
{
/**
* @var string
*/
private $author;
/**
* @var string
*/
private $title;
public function __construct(string $title, string $author)
{
$this->author = $author;
$this->title = $title;
}
public function getAuthor(): string
{
return $this->author;
}
public function getTitle(): string
{
return $this->title;
}
public function getAuthorAndTitle(): string
{
return $this->getTitle().' by '.$this->getAuthor();
}
}
<?php
//bookList.php
namespace DesignPatterns\Behavioral\Iterator;
class BookList implements \Countable, \Iterator
{
/**
* @var Book[]
*/
private $books = [];
/**
* @var int
*/
private $currentIndex = 0;
public function addBook(Book $book)
{
$this->books[] = $book;
}
public function removeBook(Book $bookToRemove)
{
foreach ($this->books as $key => $book) {
if ($book->getAuthorAndTitle() === $bookToRemove->getAuthorAndTitle()) {
unset($this->books[$key]);
}
}
$this->books = array_values($this->books);
}
public function count(): int
{
return count($this->books);
}
public function current(): Book
{
return $this->books[$this->currentIndex];
}
public function key(): int
{
return $this->currentIndex;
}
public function next()
{
$this->currentIndex++;
}
public function rewind()
{
$this->currentIndex = 0;
}
public function valid(): bool
{
return isset($this->books[$this->currentIndex]);
}
}
<?php
//test.php
namespace DesignPatterns\Behavioral\Iterator\Tests;
use DesignPatterns\Behavioral\Iterator\Book;
use DesignPatterns\Behavioral\Iterator\BookList;
use DesignPatterns\Behavioral\Iterator\BookListIterator;
use DesignPatterns\Behavioral\Iterator\BookListReverseIterator;
use PHPUnit\Framework\TestCase;
class IteratorTest extends TestCase
{
public function testCanIterateOverBookList()
{
$bookList = new BookList();
$bookList->addBook(new Book('Learning PHP Design Patterns', 'William Sanders'));
$bookList->addBook(new Book('Professional Php Design Patterns', 'Aaron Saray'));
$bookList->addBook(new Book('Clean Code', 'Robert C. Martin'));
$books = [];
foreach ($bookList as $book) {
$books[] = $book->getAuthorAndTitle();
}
$this->assertEquals(
[
'Learning PHP Design Patterns by William Sanders',
'Professional Php Design Patterns by Aaron Saray',
'Clean Code by Robert C. Martin',
],
$books
);
}
public function testCanIterateOverBookListAfterRemovingBook()
{
$book = new Book('Clean Code', 'Robert C. Martin');
$book2 = new Book('Professional Php Design Patterns', 'Aaron Saray');
$bookList = new BookList();
$bookList->addBook($book);
$bookList->addBook($book2);
$bookList->removeBook($book);
$books = [];
foreach ($bookList as $book) {
$books[] = $book->getAuthorAndTitle();
}
$this->assertEquals(
['Professional Php Design Patterns by Aaron Saray'],
$books
);
}
public function testCanAddBookToList()
{
$book = new Book('Clean Code', 'Robert C. Martin');
$bookList = new BookList();
$bookList->addBook($book);
$this->assertCount(1, $bookList);
}
public function testCanRemoveBookFromList()
{
$book = new Book('Clean Code', 'Robert C. Martin');
$bookList = new BookList();
$bookList->addBook($book);
$bookList->removeBook($book);
$this->assertCount(0, $bookList);
}
}
备忘录模式(memento)
在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。又叫做快照模式(Snapshot Pattern)或Token模式
- 优点
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
- 实现了信息的封装,使得用户不需要关心状态的保存细节
- 缺点
- 消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
- 应用场景
- 需要保存/恢复数据的相关状态场景
- 应用实例
- 后悔药
- 打游戏时的存档
- Windows 里的 ctri + z
- IE 中的后退
- Python培训的事务管理
代码一:备忘录模式
<?php
/**
* Created by PhpStorm.
*/
//游戏角色
class GameRole
{
#region 游戏角色状态属性(生命力、攻击力、防御力)
public $liveLevel;
public $attackLevel;
public $defenseLevel;
#endregion
//保存状态
public function SaveState()
{
return (new RoleStateMemento($this->liveLevel,$this->attackLevel,$this->defenseLevel));
}
//恢复状态
public function RecoveryState(RoleStateMemento $_memento)
{
$this->liveLevel = $_memento->liveLevel;
$this->attackLevel = $_memento->attackLevel;
$this->defenseLevel = $_memento->defenseLevel;
}
//------------其他属性及操作--------------
//获得初始状态
public function GetInitState()
{
$this->defenseLevel = 100;
$this->attackLevel = 100;
$this->liveLevel = 100;
}
//状态显示
public function StateDisplay()
{
echo "角色状态:<br/>";
echo "生命力:{$this->liveLevel}<br/>";
echo "攻击力:{$this->attackLevel}<br/>";
echo "防御力:{$this->defenseLevel}<br/>";
}
//被攻击
public function BeenAttack()
{
$this->liveLevel -= 9.5;
if($this->liveLevel<=0)
{
$this->liveLevel = 0;
echo "呃,该角色阵亡了!<br/>";
echo "Game Over!<br/>";
return;
}
$this->attackLevel -= 1.1;
if($this->attackLevel<=0)
{
$this->attackLevel = 0;
}
$this->defenseLevel -= 0.5;
if($this->defenseLevel<=0)
{
$this->defenseLevel = 0;
}
}
}
//角色状态存储箱类
class RoleStateMemento
{
public $liveLevel;
public $attackLevel;
public $defenseLevel;
public function RoleStateMemento($_ll,$_al,$_dl)
{
$this->liveLevel=$_ll;
$this->attackLevel=$_al;
$this->defenseLevel=$_dl;
}
}
//游戏角色状态管理者类
class RoleStateManager
{
public $memento;
}
//开战前
$ufo = new GameRole();
$ufo->GetInitState();
echo "<span style='color:#ff0000'>----------------开战前-----------------</span><br/>";
$ufo->StateDisplay();
//保存进度
$roleMan = new RoleStateManager();
$roleMan->memento = $ufo->SaveState();
echo "<span style='color:#ff0000'>----------------战斗中-----------------</span><br/>";
$num=1;
//大战Boss5个回合
for ($i = 0; $i <13;$i++ )
{
echo "-------------第{$num}回合-------------<br/>";
$ufo->BeenAttack();
$ufo->StateDisplay();
$num++;
//角色阵亡
if($ufo->liveLevel<=0)
{
break;
}
}
echo "<span style='color:#ff0000'>----------------恢复状态-----------------</span><br/>";
//恢复之前状态
$ufo->RecoveryState($roleMan->memento);
$ufo->StateDisplay();
代码二:备忘录模式
<?php
//备忘录模式定义:备忘录模式又叫做快照模式或Token模式,在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
// 角色:
//1.发起人:负责创建一个备忘录,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。发起人能够依据须要决定备忘录存储自己的哪些内部状态。
//2.备忘录:负责存储发起人对象的内部状态,并能够防止发起人以外的其它对象訪问备忘录。备忘录有两个接口:管理者仅仅能看到备忘录的窄接口,他仅仅能将备忘录传递给其它对象。发起人却可看到备忘录的宽接口。同意它訪问返回到先前状态所须要的全部数据。
//3.管理者:负责存取备忘录,不能对内容进行訪问或者操作。
/**
* 发起人类
*/
class Sponsor{
public $time = 36000;
public $title = "奋斗者";
public $money = 0;
public function subTime(){
$this->time --;
}
public function addMoney(){
$this->money += 1000;
}
public function changeTitle($title){
$this->title = $title;
}
/**
* 备份当前的基础数据属性
* @dateTime 2017-02-13
*/
public function backup(){
return new Backup($this->time,$this->money,$this->title);
}
/**
* 数据还原
* @dateTime 2017-02-13
*/
public function reback(Backup $backup){
$this->time = $backup->time;
$this->title = $backup->title;
$this->money = $backup->money;
}
}
/**
* 存储类
*/
class Backup{
public $time;
public $title;
public $money;
public function __construct($time,$money,$title){
$this->time = $time;
$this->title = $title;
$this->money = $money;
}
}
/**
* 管理者类
*/
class Manager{
public $data;
}
//客户端应用
$Sponsor = new Sponsor();
//备份初始状态
$Manager = new Manager();
$Manager->data = $Sponsor -> backup();
while($Sponsor->time > 0){
$Sponsor -> addMoney();
$Sponsor -> subTime();
if($Sponsor->money >= 1000000)
$Sponsor -> changeTitle("骄傲者");
if($Sponsor->money >= 10000000)
$Sponsor -> changeTitle("能力者");
if($Sponsor->money >= 100000000)
$Sponsor -> changeTitle("成功者");
}
var_dump($Sponsor);
//还原初始状态
$Sponsor->reback($Manager->data);
var_dump($Sponsor);
模版模式(template_method)
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行
- 优点
- 封装不变部分,扩展可变部分
- 提取公共代码,便于维护
- 行为由父类控制,子类实现
- 缺点
- 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大
代码一:模板模式
//Game.class.php (抽象父类,模板类)
<?php
namespace Template;
abstract class Game
{
abstract function initialize();
abstract function startPlay();
abstract function endPlay();
public final function play()
{
//初始化游戏
$this->initialize();
//开始游戏
$this->startPlay();
//结束游戏
$this->endPlay();
}
}
//Cricket.class.php (具体子类)
<?php
namespace Template;
class Cricket extends Game
{
public function initialize()
{
echo "Cricket Game Initialized! Start playing.";
echo '<br/>';
}
public function startPlay()
{
echo "Cricket Game Started. Enjoy the game!";
echo '<br/>';
}
public function endPlay()
{
echo "Cricket Game Finished!";
echo '<br/>';
}
}
//Football.class.php (具体子类)
<?php
namespace Template;
class Football extends Game
{
public function initialize()
{
echo "Football Game Initialized! Start playing.";
echo '<br/>';
}
public function startPlay()
{
echo "Football Game Started. Enjoy the game!";
echo '<br/>';
}
public function endPlay()
{
echo "Football Game Finished!";
echo '<br/>';
}
}
//template.php
<?php
/*
一个算法的结构即可重定义该算法的某些特定步骤。
*/
spl_autoload_register(function ($className){
$className = str_replace('\\','/',$className);
include $className.".class.php";
});
use Template\Cricket;
use Template\Football;
$game = new Cricket();
$game->play();
$game2 = new Football();
$game2->play();
代码二:模板模式
// IHook.php
<?php
abstract class IHook
{
protected $hook;
protected $fullCost;
public function templateMethod($fullCost, $hook)
{
$this->fullCost = $fullCost;
$this->hook = $hook;
$this->addGoods();
$this->addShippingHook();
$this->displayCost();
}
protected abstract function addGoods();
protected abstract function addShippingHook();
protected abstract function displayCost();
}
//Concrete.php
<?php
class Concrete extends IHook
{
protected function addGoods()
{
$this->fullCost = $this->fullCost * 0.8;
}
protected function addShippingHook()
{
if(!$this->hook)
{
$this->fullCost += 12.95;
}
}
protected function displayCost()
{
echo "您需要支付: " . $this->fullCost . '元<br />';
}
}
//Client.php
<?php
function autoload($class_name)
{
include $class_name . '.php';
}
class Client
{
private $totalCost;
private $hook;
public function construct($goodsTotal)
{
$this->totalCost = $goodsTotal;
$this->hook = $this->totalCost >= 200;
$concrete = new Concrete();
$concrete->templateMethod($this->totalCost, $this->hook);
}
}
$worker = new Client(100);
$worker = new Client(200);
访问者模式(memento)
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
代码一:访问者模式
//Computer.class.php (电脑类)
<?php
namespace Visitor;
class Computer
{
protected $_items = [];
public function add(Unit $unit)
{
$this->_items[] = $unit;
}
public function accept(Visitor $visitor)
{
foreach ($this->_items as $item) {
$item->accept($visitor);
}
}
}
//Unit.class.php (组件抽象父类)
<?php
namespace Visitor;
abstract class Unit
{
public abstract function getName();
public function accept(Visitor $visitor)
{
$class = get_class($this);
$classPath = explode('\\',$class);
$className = array_pop($classPath);
$method = 'visit'.$className;
if (method_exists($visitor, $method)){
$visitor->$method($this);
echo '<br/>';
}
}
}
//Cpu.class.php (具体组件:cpu)
<?php
namespace Visitor;
class Cpu extends Unit
{
public function getName()
{
return 'i am cpu';
}
}
//Memory.class.php(具体组件:内存)
<?php
namespace Visitor;
class Memory extends Unit
{
public function getName()
{
return 'i am Memory';
}
}
//Keyboard.class.php(具体组件:键盘)
<?php
namespace Visitor;
class Memory extends Unit
{
public function getName()
{
return 'i am Memory';
}
}
//Visitor.class.php (访问者类)
<?php
namespace Visitor;
class Visitor
{
public function visitCpu(Cpu $cpu)
{
echo "hello, " . $cpu->getName() . "\n";
}
public function visitMemory(Memory $memory)
{
echo "hello, " . $memory->getName() . "\n";
}
public function visitKeyboard(Keyboard $keyboard)
{
echo "hello, " . $keyboard->getName() . "\n";
}
}
//visitor.php (客户端类)
<?php
spl_autoload_register(function ($className){
$className = str_replace('\\','/',$className);
include $className.".class.php";
});
use Visitor\Computer;
use Visitor\Cpu;
use Visitor\Memory;
use Visitor\Keyboard;
use Visitor\Visitor;
$computer = new Computer();
$computer->add(new Cpu());
$computer->add(new Memory());
$computer->add(new Keyboard());
$computer->accept(new Visitor());