23三种设计模式详解

本文详细介绍了设计模式的三大类别:创建型、结构型和行为型模式,涵盖单例模式、工厂模式、抽象工厂模式、策略模式、观察者模式、适配器模式、注册模式、建造者模式、原型模式、装饰器模式、代理模式、外观模式、桥接模式等多个经典模式。每种模式都阐述了其动机、优缺点、使用场景及实现步骤,帮助读者理解和运用这些设计模式来优化软件结构和提高代码质量。
摘要由CSDN通过智能技术生成

设计模式分为三大类:

  创建型模式,共五种:工厂方法模式、抽象工厂模式单例模式、建造者模式、原型模式。

  结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

一、单列模式

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式是一种常见的设计模式,在计算机系统中,线程池、缓存、日志对象、对话框、打印机、数据库操作、显卡的驱动程 序常被设计成单例。单例模式分3种:懒汉式单例、饿汉式单例、登记式单例。
单例模式有以下3个特点:
1.只能有一个实例。
2.必须自行创建这个实例。
3.必须给其他对象提供这一实例。

<?php

 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 world');

$ob->setname('good morning');

echo $oa->getname();//good morning

echo $ob->getname();//good morning

 二、工厂模式
工厂模式是我们最常用的实例化对象模式,是用工厂方法代替new 操作的一种模式。
使用工厂模式的好处是,如果你想要更改所实例化的类名等,则只需更改该工厂方法内容即可,
不需逐一寻找代码中具体实例 化的地方(new 处)修改了。为系统结构提供灵活的动态扩展机制,减少了耦合

<?php

header('Content-Type:text/html;charset=utf-8');/**
*简单工厂模式(静态工厂方法模式)
*//**
* Interface people 人类
*/
interface people
{
public function say();
}/**
* Class man 继承 people 的男人类
*/class man implements people
{
// 具体实现people 的say 方法
public function say()
{
echo '我是男人<br>';
}
}/**
* Class women 继承 people 的女人类
*/class women implements people
{
// 具体实现people 的say 方法
public function say()
{
echo '我是女人<br>';
}
}/**
* Class SimpleFactoty 工厂类
*/class SimpleFactoty
{
// 简单工厂里的静态方法-用于创建男人对象
static function createMan()
{
return new man();
}
// 简单工厂里的静态方法-用于创建女人对象
static function createWomen()
{
return new women();
}
}/**
* 具体调用
*/
$man = SimpleFactoty::createMan();
$man->say();
$woman = SimpleFactoty::createWomen();
$woman->say();

三、工厂抽象模式

抽象工厂模式(Abstract Factory Pattern)
是围绕一个超级工厂创建其他工厂。
该超级工厂又称为其他工厂的工厂。
这种类型的设计模式属于创建型模式,
它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,
不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

介绍
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。
关键代码:在一个工厂里聚合多个同类产品。
应用实例:工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、
时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,
这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,
但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),
每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具体工厂)
都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),
这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。

优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

使用场景: 1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。

注意事项:产品族难扩展,产品等级易扩展。


<?php

class System{}

class Soft{}

class MacSystem extends System{}

class MacSoft extends Soft{}

class WinSystem extends System{}

class WinSoft extends Soft{}

/**

 * AbstractFactory class[抽象工厂模式]

 * @author 侯蜀黍

 */

interface AbstractFactory {

    public function CreateSystem();

    public function CreateSoft();

}

class MacFactory implements AbstractFactory{

    public function CreateSystem(){ return new MacSystem(); }

    public function CreateSoft(){ return new MacSoft(); }

}

class WinFactory implements AbstractFactory{

    public function CreateSystem(){ return new WinSystem(); }

    public function CreateSoft(){ return new WinSoft(); }

}

//@test:创建工厂->用该工厂生产对应的对象

//创建MacFactory工厂

$MacFactory_obj = new MacFactory();

//用MacFactory工厂分别创建不同对象

var_dump($MacFactory_obj->CreateSystem());//输出:object(MacSystem)#2 (0) { }

var_dump($MacFactory_obj->CreateSoft());// 输出:object(MacSoft)#2 (0) { }

//创建WinFactory

$WinFactory_obj = new WinFactory();

//用WinFactory工厂分别创建不同对象

var_dump($WinFactory_obj->CreateSystem());//输出:object(WinSystem)#3 (0) { }

var_dump($WinFactory_obj->CreateSoft());//输出:object(WinSoft)#3 (0) { }



​

四、策源模式

策源模式
描述下策源模式特点:
实现步骤:
策略模式指的是程序中涉及决策控制的一种模式
1.定义抽象角色类(定义好各个实现的共同抽象方法)
2.定义具体策略类(具体实现父类的共同方法)
3.定义环境角色类(私有化申明抽象角色变量,重载构造方法,执行抽象方法)

<?php

abstract class baseAgent { //抽象策略类
abstract function PrintPage();
}
//用于客户端是 IE时调用的类(环境角色)
class ieAgent extends baseAgent {
function PrintPage() {
return 'IEEEEEEEEEEEEEEEE';
}
}
//用于客户端不是IE 时调用的类(环境角色)
class otherAgent extends baseAgent {
function PrintPage() {
return 'not IE';
}
}

class Browser { //具体策略角色
public function call($object) {
return $object->PrintPage ();
}
}

$bro = new Browser ();

echo $bro->call (new ieAgent ());

五、观察者模式

1.观察者模式(Observer),当一个对象状态发生变化时,依赖它的对象全部会收到通知,并自动更新。
 2:场景:一个事件发生后,要执行一连串更新操作。传统的编程方式,就是在事件的代码之后直接加入处理的逻辑。当更新的逻 辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件的主体代码。
 3:观察者模式实现了低耦合,非侵入式的通知与更新机制。 定义一个事件触发抽象类。

<?php

abstract class EventGenerator{
private $observers = array();
function addObserver(Observer $observer){
$this->observers[]=$observer;
}

function notify(){
foreach ($this->observers as $observer){
$observer->update();
}
}
}

//定义一个观察者接口
interface Observer{
function update();//这里就是在事件发生后要执行的逻辑}//一个实现了EventGenerator 抽象类的类,用于具体定义某个 发生的事件
//实现

class Event extends EventGenerator{
function triger(){
echo "Event<br>";
}
}
class Observer1 implements Observer{
function update(){
echo "逻辑 1<br>";
}
}
class Observer2 implements Observer{
function update(){
echo "逻辑 2<br>";
}
}

$event = new Event();
$event->addObserver(new Observer1());
$event->addObserver(new Observer2());
$event->triger();
$event->notify();









}

六、适配器模式

适配器模式将各种截然不同的函数接口封装成统一的API。 PHP 中的数据库操作有 MySQL,MySQLi,PDO 三种,可以用适配器模式统一成一致,使不同的数据库操作,
统一成一样的 API。 类似的场景还有cache 适配器,可以将memcache,redis,file,apc等不同的缓存函数,统一成一致。 首先定义一个接口(有几个方法,以及相应的参数)。然后,有几种不同的情况,就写几个类实现该接口。将完成相似功能的函数, 统一成一致的方法。

<?php
/* 
适配器模式
将各种截然不同的函数接口封装成统一的API。 
PHP 中的数据库操作有 MySQL,MySQLi,PDO 三种,
可以用适配器模式统一成一致,使不同的数据库操作,
统一成一样的 API。 类似的场景还有cache 适配器,
可以将memcache,redis,file,apc等不同的缓存函数,统一成一致。 
首先定义一个接口(有几个方法,以及相应的参数)。
然后,有几种不同的情况,就写几个类实现该接口。
将完成相似功能的函数, 统一成一致的方法。

 */
 
namespace IMooc;
interface IDatabase
{
function connect($host, $user, $passwd, $dbname);
function query($sql);
function close();
}

//MySQL
<?php
namespace IMooc\Database;
use IMooc\IDatabase;
class MySQL implements IDatabase
{
protected $conn;
function connect($host, $user, $passwd, $dbname)
{
$conn = mysql_connect($host, $user, $passwd);
mysql_select_db($dbname, $conn);
$this->conn = $conn;
}
function query($sql)
{
$res = mysql_query($sql, $this->conn);
return $res;
}
function close()
{
mysql_close($this->conn);
}
}

//MySQLi
<?php
namespace IMooc\Database;
use IMooc\IDatabase;
class MySQLi implements IDatabase
{
protected $conn;
function connect($host, $user, $passwd, $dbname)
{
$conn = mysqli_connect($host, $user, $passwd, $dbname);
$this->conn = $conn;
}
function query($sql)
{
return mysqli_query($this->conn, $sql);
}
function close()
{
mysqli_close($this->conn);
}
}

七、注册模式

注册模式,解决全局共享和交换对象。已经创建好的对象,挂在到某个全局可以使用的数组上,
在需要使用的时候,直接从该 数组上获取即可。将对象注册到全局的树上。任何地方直接去访问。

<?php
/* 
注册模式,解决全局共享和交换对象。
已经创建好的对象,挂在到某个全局可以使用的数组上,
在需要使用的时候,直接从该 数组上获取即可。
将对象注册到全局的树上。任何地方直接去访问。
 */
class Register
{
protected static $objects;

function set($alias,$object)
{
//将对象注册到全局的树上  //将对象放到树上
self::$objects[$alias]=$object;
}
 
static function get($name){
//获取某个注册到树上的对象 
return self::$objects[$name];
}


function _unset($alias){
//移除某个注册到树上的对象。
unset(self::$objects[$alias]);
}
}

class t1 extends Register{
function test(){
	echo '1212';exit;
}
}


$p = new t1();
$p->set('a',$p);
$newp = $p->get('a');

$res = $newp->test();
var_dump($res);

八、建造者模式

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。

介绍
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;
由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

何时使用:一些基本部件不会变,而其组合经常变化的时候。

如何解决:将变与不变分离开。

关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。

应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 

优点: 1、建造者独立,易扩展。 2、便于控制细节风险。

缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。

使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。

注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

建造者模式一般认为有四个角色:
     1.产品角色,产品角色定义自身的组成属性

     2.抽象建造者,抽象建造者定义了产品的创建过程以及如何返回一个产品

     3.具体建造者,具体建造者实现了抽象建造者创建产品过程的方法,给产品的具体属性进行赋值定义

     4.指挥者,指挥者负责与调用客户端交互,决定创建什么样的产品

<?php
/* 


 */
 
 /**

 *

 * 产品本身

 */

class Product {

    private $_parts;

    public function __construct() { $this->_parts = array(); }

    public function add($part) { return array_push($this->_parts, $part); }

}

/**

 * 建造者抽象类

 *

 */

abstract class Builder {

    public abstract function buildPart1();

    public abstract function buildPart2();

    public abstract function getResult();

}

/**

 *

 * 具体建造者

 * 实现其具体方法

 */

class ConcreteBuilder extends Builder { 

    private $_product;

    public function __construct() { $this->_product = new Product(); }

    public function buildPart1() { $this->_product->add("Part1"); }

    public function buildPart2() { $this->_product->add("Part2"); }

    public function getResult() { return $this->_product; }

}

 /**

  *

  *导演者

  */

class Director {

    public function __construct(Builder $builder) {

        $builder->buildPart1();//导演指挥具体建造者生产产品

        $builder->buildPart2();

    }

}

// client

$buidler = new ConcreteBuilder();

$director = new Director($buidler);

$product = $buidler->getResult();

echo "<pre>";

var_dump($product);

echo "</pre>";

/*输出: object(Product)#2 (1) {

["_parts":"Product":private]=>

array(2) {

    [0]=>string(5) "Part1"

    [1]=>string(5) "Part2"

}

} */

九、原型模式

原型模式(Prototype)
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

介绍
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

主要解决:在运行期建立和删除原型。

何时使用: 1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。

关键代码: 1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone(),在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。

应用实例: 1、细胞分裂。 2、JAVA 中的 Object clone() 方法。

优点: 1、性能提高。 2、逃避构造函数的约束。

缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。

使用场景: 1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。

注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

概念理解:原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。适用于大对象的创建,因为创建一个大对象需要很大的开销,如果每次new就会消耗很大,原型模式仅需内存拷贝即可。

主要角色:

Prototype(抽象原型角色):声明一个克隆自身的接口

Concrete Prototype(具体原型角色):实现一个克隆自身的操作



 */
interface Prototype

{

    public function shallowCopy();

    public function deepCopy();

}

class ConcretePrototype implements Prototype

{

    private $_name;

    public function __construct($name)

    {

        $this->_name = $name;

    }

    public function setName($name)

    {

        $this->_name = $name;

    }

    public function getName()

    {

        return $this->_name;

    }

    /**

     * 浅拷贝

     * */

    public function shallowCopy()

    {

        return clone $this;

    }

    /**

     * 深拷贝

     * */

    public function deepCopy()

    {

        $serialize_obj = serialize($this);

        $clone_obj = unserialize($serialize_obj);

        return $clone_obj;

    }

}

class Demo

{

    public $string;

}

class UsePrototype

{

    public function shallow()

    {

        $demo = new Demo();

        $demo->string = "susan";

        $object_shallow_first = new ConcretePrototype($demo);

        $object_shallow_second = $object_shallow_first->shallowCopy();

        var_dump($object_shallow_first->getName());

        echo '<br/>';

        var_dump($object_shallow_second->getName());

        echo '<br/>';

        $demo->string = "sacha";

        var_dump($object_shallow_first->getName());

        echo '<br/>';

        var_dump($object_shallow_second->getName());

        echo '<br/>';

    }

    public function deep()

    {

        $demo = new Demo();

        $demo->string = "Siri";

        $object_deep_first = new ConcretePrototype($demo);

        $object_deep_second = $object_deep_first->deepCopy();

        var_dump($object_deep_first->getName());

        echo '<br/>';

        var_dump($object_deep_second->getName());

        echo '<br/>';

        $demo->string = "Demo";

        var_dump($object_deep_first->getName());

        echo '<br/>';

        var_dump($object_deep_second->getName());

        echo '<br/>';

    }

}

$up = new UsePrototype;

$up->shallow(); //浅拷贝

echo '<hr>';

$up->deep();    //深拷贝

十、装饰器模式

1.装饰器模式(Decorator),可以动态地添加修改类的功能
2.一个类提供了一项功能,如果要在修改并添加额外的功能,传统的编程模式,需要写一个子类继承它,并重新实现类的方法
3.使用装饰器模式,仅需在运行时添加一个装饰器对象即可实现,可以实现最大的灵活性

<?php

/**
 * 输出一个字符串
 * 装饰器动态添加功能
 * Class EchoText
 */
class EchoText
{
    protected $decorator = [];

    public function Index()
    {
        //调用装饰器前置操作
        $this->beforeEcho();
        echo "你好,我是装饰器。";
        //调用装饰器后置操作
        $this->afterEcho();
    }

    //增加装饰器
    public function addDecorator(Decorator $decorator)
    {
        $this->decorator[] = $decorator;
    }

    //执行装饰器前置操作 先进先出原则
    protected function beforeEcho()
    {
        foreach ($this->decorator as $decorator)
            $decorator->before();
    }

    //执行装饰器后置操作 先进后出原则
    protected function afterEcho()
    {
        $tmp = array_reverse($this->decorator);
        foreach ($tmp as $decorator)
            $decorator->after();
    }
}


/**
 * 装饰器接口
 * Class Decorator
 */
interface Decorator
{
    public function before();

    public function after();
}

/**
 * 颜色装饰器实现
 * Class ColorDecorator
 */
class ColorDecorator implements Decorator
{
    protected $color;

    public function __construct($color)
    {
        $this->color = $color;
    }

    public function before()
    {
        echo "<dis style='color: {$this->color}'>";
    }

    public function after()
    {
        echo "</div>";
    }
}

/**
 * 字体大小装饰器实现
 * Class SizeDecorator
 */
class SizeDecorator implements Decorator
{
    protected $size;

    public function __construct($size)
    {
        $this->size = $size;
    }

    public function before()
    {
        echo "<dis style='font-size: {$this->size}px'>";
    }

    public function after()
    {
        echo "</div>";
    }
}

//实例化输出类
$echo = new EchoText();
//增加装饰器
$echo->addDecorator(new ColorDecorator('red'));
//增加装饰器
$echo->addDecorator(new SizeDecorator('22'));
//输出
$echo->Index();

十一、代理模式

代理模式(Proxy Pattern) :

  给某一个对象提供一个代 理,并由代理对象控制对原对象的引用。代理模式的英 文叫做Proxy或Surrogate,它是一种对象结构型模式

模式动机:
  在某些情况下,一个客户不想或者不能直接引用一个对 象,此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。
  通过引入一个新的对象(如小图片和远程代理对象)来实现对真实对象的操作或者将新的对 象作为真实对象的一个替身,这种实现机制即 为代理模式,通过引入代理对象来间接访问一 个对象,这就是代理模式的模式动机。

代理模式包含如下角色:
  抽象主题角色(Subject):定义了RealSubject和Proxy公用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。
  真正主题角色(RealSubject):定义了Proxy所代表的真实实体。
  代理对象(Proxy):保存一个引用使得代理可以访问实体,并提供一个与RealSubject接口相同的接口,这样代理可以用来代替实体(RealSubject)。

UML图:

../_images/Proxy.jpg

 

<?php
header("Content-type:text/html;Charset=utf-8");

//定义RealSubject和Proxy共同具备的东西
interface Subject{
    function say();
    function run();
}

class RealSubject implements Subject{
    private $name;

    function __construct($name){
        $this->name = $name;
    }

    function say(){
        echo $this->name."在吃饭<br>";
    }
    function run(){
        echo $this->name."在跑步<br>";
    }
}
class Proxy implements Subject{
    private $realSubject = null;
    function __construct(RealSubject $realSubject = null){
        if(empty($realSubject)){
            $this->realSubject = new RealSubject();
        }else{
            $this->realSubject = $realSubject;
        }
    }
    function say(){
        $this->realSubject->say();
    }
    function run(){
        $this->realSubject->run();
    }
}

//测试
$subject = new RealSubject("张三");
$proxy = new Proxy($subject);
$proxy->say();
$proxy->run();
/*
张三在吃饭
张三在跑步
*/
?>

十二、外观模式 

外观模式(Facade pattern),为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。外观模式又称为门面模式,它是一种对象结构型模式,遵循迪米特法则,又称最少知道原则。

模式动机

现代的软件系统都非常复杂,尽管我们已经想尽一切方法将其“分而治之”,把一个系统划分为好几个较小的子系统了,但是仍然可能会存在这样的问题:子系统内有非常多的类,客户端往往需要和许多对象打交道之后 才能完成想要完成的功能。

在我们的生活中医院就是这样的。一般的医院都会分为挂号、门诊、化验、收费、取药等。看病的病人要想治好自己的病(相当于一个客户端想要实现自己的功能)就要和医院的各个部门打交道。首先,病人需要挂号,然后门诊,如果医生要求化验的话,病人就要去化验,然后再回到门诊室,最后拿药,经过一系列复杂的过程后才能完成看病的过程。如下图所示:

解决方案:解决这种不便的方式就是引入外观模式。如果我们在医院设立一个接待员的话,病人只负责和接待员接触,由接待员负责与医院的各个部门打交道,让接待员完全帮助我们实现“看病”各个操作,如下图所示:

 

外观模式的基本组成:
外观角色 Facade:模式的核心,被客户 Client 调用,知道各个子系统的概念。根据客户角色的需求定制功能组合。
子系统角色 SubSystem:实现子系统的功能。
客户角色 Client:调用 Facade 角色获取相应的功能。

优点

  • 降低系统的复杂程度,对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入外观模式,客户代码将变得很简单,与之关联的对象也很少。
  • 实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。
  • 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类。

缺点

  • 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类。
  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

使用场景

  • 当要为一个复杂子系统提供一个简单接口时可以使用外观模式。该接口可以满足大多数用户的需求,而且用户也可以越过外观类直接访问子系统。
  • 客户程序与多个子系统之间存在很大的依赖性。引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性。
  • 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。

实例

下面以电脑开机为例子,正常情况下,我们只需要按下开机键即可,其他操作,比如 BIOS、打开操作系统这些操作是不需要用户关心的。

<?php
// 外观模式
// 下面以电脑开关机为例子,正常情况下,我们只需要按下开机键即可,其他操作,比如 BIOS、打开操作系统这些操作是不需要用户关心的。
class OperatingSystem {
    public function open() {
        echo '打开操作系统 ';
    }

    public function shutdown() {
        echo '关闭操作系统 ';
    }

    public function login() {
        echo '登录操作系统 ';
    }
}

class Bios {
    // 硬件自检
    public function hardware_check() {
        echo '硬件自检 ';
    }
    // 启动操作系统
    public function launch(OperatingSystem $os) {
        echo '启动操作系统 ';
    }
    // 电源关闭
    public function power_down() {
        echo '电源关闭 ';
    }
}

class Facade {
    private $os;
    private $bios;

    public function __construct() {
        $this->bios = new Bios;
        $this->os = new OperatingSystem;
    }

    public function turn_on() {
        $this->bios->hardware_check();
        $this->bios->launch($this->os);
        $this->os->open();
        $this->os->login();
    }

    public function turn_off() {
        $this->os->shutdown();
        $this->bios->power_down();
    }
}

// client 
$facade = new Facade();

// computer on
$facade->turn_on();
echo "<br>";
// computer off
$facade->turn_off();

 十三、桥接模式

1、桥接模式

桥连模式:将抽象部分与实现部分分离,使它们都可以独立的变化。它是一种结构性模式,又称柄体(Handle and body)模式或者接口(Interface)模式。        当一个抽象可能有多个实现时,通常用继承来协调他们。抽象类的定义对该抽象的接口。而具体的子类则用不同的方式加以实现,但是此方法有时不够灵活。继承机制将抽象部分与他的视线部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改、扩充和充用。

理解桥接模式,重点需要理解如何将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。
•抽象化:抽象化就是忽略一些信息,把不同的实体当作同样的实体对待。在面向对象中,将对象的共同性质抽取出来形成类的过程即为抽象化的过程。
•实现化:针对抽象化给出的具体实现,就是实现化,抽象化与实现化是一对互逆的概念,实现化产生的对象比抽象化更具体,是对抽象化事物的进一步具体化的产物。
•脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。 
2、适用性

1). 你不希望在抽象和他的实现部分之间有一个固定的邦定关系,如在程序的运行时刻实现部分应该可以被选择或者切换。

2). 类的抽象以及他的视像都可以通过生成子类的方法加以扩充。这时bridge模式使你可以对不同的抽象接口

      和实现部分进行组合,并对他们进行扩充。

3). 对一个抽象的实现部分的修改应该对客户不产生影响,即客户的代码不需要重新编译。

4). 你想对客户完全隐藏抽象的实现部分。

5). 你想在多个实现间 共享实现,但同时要求客户并不知道这一点。
3、结构

4、模式组成

抽象类(Abstraction):定义抽象类的接口,维护一个指向Implementor类型对象的指针

扩充抽象类(RefinedAbstraction):扩充由Abstraction定义的接口

实现类接口(Implementor):定义实现类的接口,该接口不一定要与 Abstraction的接口完全一致;事实上这两个接口可以完全不同。一般来讲, Implementor接口仅提供基本操作,而 Abstraction则定义了基于这些基本操作的较高层次的操作。

具体实现类(ConcreteImplementor):实现Implementor接口并定义它的具体实现。
5、效果

Bridge模式有以下一些优点:
1) 分离接口及其实现部分 一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现。将Abstraction与Implementor分离有助于降低对实现部分编译时刻的依赖性,当改变一个实现类时,并不需要重新编译 Abstraction类和它的客户程序。为了保证一个类库的不同版本之间的二进制兼容性,一定要有这个性质。另外,接口与实现分离有助于分层,从而产生更好的结构化系统,系统的高层部分仅需知道Abstraction和Implementor即可。
2) 提高可扩充性 你可以独立地对Abstraction和Implementor层次结构进行扩充。
3 ) 实现细节对客户透明 你可以对客户隐藏实现细节,例如共享 Implementor对象以及相应的引用计数机制(如果有的话) 。


桥接模式的缺点
•桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
•桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。 
6、实现

模拟毛笔:

        现需要提供大中小3种型号的画笔,能够绘制5种不同颜色,如果使用蜡笔,我们需要准备3*5=15支蜡笔,也就是说必须准备15个具体的蜡笔类。而如果使用毛笔的话,只需要3种型号的毛笔,外加5个颜料盒,用3+5=8个类就可以实现15支蜡笔的功能。

       实际上,蜡笔和毛笔的关键一个区别就在于笔和颜色是否能够分离。即将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化"。关键就在于能否脱耦。蜡笔的颜色和蜡笔本身是分不开的,所以就造成必须使用15支色彩、大小各异的蜡笔来绘制图画。而毛笔与颜料能够很好的脱耦,各自独立变化,便简化了操作。在这里,抽象层面的概念是:"毛笔用颜料作画",而在实现时,毛笔有大中小三号,颜料有红绿蓝黑白等5种,于是便可出现3×5种组合。每个参与者(毛笔与颜料)都可以在自己的自由度上随意转换。

         蜡笔由于无法将笔与颜色分离,造成笔与颜色两个自由度无法单独变化,使得只有创建15种对象才能完成任务。

Bridge模式将继承关系转换为组合关系,从而降低了系统间的耦合,减少了代码编写量。

 

7.与其他模式的区别

1)抽象工厂(Abstract Factory 模式可以用来创建和配置一个特定的Bridge模式。

2)Adapter模式 用来帮助无关的类协同工作,它通常在系统设计完成后才会被使用。然而,Bridge模式则是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。

3)桥接模式与装饰的区别:

装饰模式:

      这两个模式在一定程度上都是为了减少子类的数目,避免出现复杂的继承关系。但是它们解决的方法却各有不同,装饰模式把子类中比基类中多出来的部分放到单独的类里面,以适应新功能增加的需要,当我们把描述新功能的类封装到基类的对象里面时,就得到了所需要的子类对象,这些描述新功能的类通过组合可以实现很多的功能组合 .

桥接模式:

       桥接模式则把原来的基类的实现化细节抽象出来,在构造到一个实现化的结构中,然后再把原来的基类改造成一个抽象化的等级结构,这样就可以实现系统在多个维度上的独立变化 。
8.总结:Bridge模式是一个非常有用的模式,也非常复杂,它很好的符合了开放-封闭原则和优先使用对象,而不是继承这两个面向对象原则。

<?php
 
/******************************Abstraction **************************/
/**
 * 
 * Abstraction抽象类的接口
 * @author guisu
 *
 */
abstract class BrushPenAbstraction {
    protected $_implementorColor = null;
 
    /**
     * 
     * Enter description here ...
     * @param Color $color
     */
    public function setImplementorColor(ImplementorColor $color) {
        $this->_implementorColor = $color;
    }
    /**
     * 
     * Enter description here ...
     */
    public abstract function operationDraw();
}
/******************************RefinedAbstraction **************************/
/**
 * 
 * 扩充由Abstraction;大毛笔
 * @author guisu
 *
 */
class BigBrushPenRefinedAbstraction extends BrushPenAbstraction {
    public function operationDraw() {
        echo 'Big and ', $this->_implementorColor->bepaint (), ' drawing';
    }
}
/**
 * 
 * 扩充由Abstraction;中毛笔
 * @author guisu
 *
 */
class MiddleBrushPenRefinedAbstraction extends BrushPenAbstraction {
    public function operationDraw() {
        echo 'Middle and ', $this->_implementorColor->bepaint (), ' drawing';
    }
}
/**
 * 
 * 扩充由Abstraction;小毛笔
 * @author guisu
 *
 */
class SmallBrushPenRefinedAbstraction extends BrushPenAbstraction {
    public function operationDraw() {
        echo 'Small and ', $this->_implementorColor->bepaint(), ' drawing';
    }
}
 
/******************************Implementor **************************/
/**
 * 实现类接口(Implementor)
 * 
 * @author mo-87
 *
 */
class ImplementorColor {
    protected $value;
 
    /**
     * 着色
     * 
     */
    public  function bepaint(){
        echo $this->value;
    }
}
/******************************oncrete Implementor **************************/
class oncreteImplementorRed extends ImplementorColor {
    public function __construct() {
        $this->value = "red";
    }
    /**
     * 可以覆盖
     */
    public function bepaint() {
        echo $this->value;
    }
}
 
class oncreteImplementorBlue extends ImplementorColor {
    public function __construct() {
        $this->value = "blue";
    }
}
 
class oncreteImplementorGreen extends ImplementorColor {
    public function __construct() {
        $this->value = "green";
    }
}
 
class oncreteImplementorWhite extends ImplementorColor {
    public function __construct() {
        $this->value = "white";
    }
}
 
class oncreteImplementorBlack extends ImplementorColor {
    public function __construct() {
        $this->value = "black";
    }
}
/**
 * 
 * 客户端程序
 * @author guisu
 *
 */
class Client {
    public static function Main() {
 
        //小笔画红色
        $objRAbstraction = new SmallBrushPenRefinedAbstraction();
        $objRAbstraction->setImplementorColor(new oncreteImplementorRed());
        $objRAbstraction->operationDraw();
    }
}
Client::Main();

 十四、组合模式

当我们的一个对象可能代表一个单一的实体,或者一个组合的实体,但是仍然需要通过同样的方式被使用时,这种情形则适合使用组合模式的设计。组合模式是一种结构型模式。当看了书上的解释之后,并不是很理解,遂去翻了翻《大化设计模式》,以下为原文截图:

总结以下组合模式的特点:

  1. 必须存在不可分割基本元素;
  2. 组合后的物体任然可以被组合。
​
<?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();


​

 十五、享元模式

减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式, * 它提供了减少对象数量从而改善应用所需的对象结构的方式。 * 享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。 * 在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。 * 优点:大大减少对象的创建,降低系统的内存,使效率提高。 * 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。 * 使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。 * 注意事项: 1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。 * 我们将通过创建 5 个对象来画出 20 个分布于不同位置的圆来演示这种模式。由于只有 5 种可用的颜色,所以 color 属性被用来检查现有的 Circle 对象。

​
<?php

namespace FlyWeight;

interface Shape
{
    public function draw();
}


class Circle implements Shape
{

    private $color;
    private $x;
    private $y;
    private $radius;

    public function __construct($color)
    {
        $this->color = $color;
    }

    public function setX($x)
    {
        $this->x = $x;
    }

    public function setY($y)
    {
        $this->y = $y;
    }

    public function setRadius($radius)
    {
        $this->radius = $radius;
    }

    public function draw()
    {
        print_r("Circle: Draw() [Color : " . $this->color .", x : " . $this->x .", y :" . $this->y .", radius :" . $this->radius);
        echo '<br/>';
    }
}

class ShapeFactory
{
    private static $data = [];

    public static function getCircle($color)
    {
        $circle = null;
        if (!isset(self::$data[$color])) {
            $circle = new Circle($color);
            self::$data[$color] = $circle;
            print_r("Creating circle of color : " . $color);
        }else{
            $circle = self::$data[$color];
        }
        return $circle;
    }
}

spl_autoload_register(function ($className){
    $className = str_replace('\\','/',$className);
    include $className.".class.php";
});



$color = [ "Red", "Green", "Blue", "White", "Black" ];
for ($i = 0; $i < 20; $i ++) {
    $circle = ShapeFactory::getCircle($color[array_rand($color)]);
    $circle->setX(rand()*100);
    $circle->setY(rand()*100);
    $circle->setRadius(100);
    $circle->draw();
}

​

十六、模块方法模式

<?php
/**
 * 模板模式
 *
 * 定义一个操作中的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构可以定义该算法的某些特定步骤
 *
 */
abstract class TemplateBase
{
    public function Method1()
    {
        echo "abstract Method <br/>";
    }
 
    public function Method2()
    {
        echo "abstract Method2<br/>";
    }
 
    public function Method3()
    {
        echo "abstract Method3<br/>";
    }
 
    public function doSomeThing()//骨架,上面三个是步骤,可以在子类中延迟实现
    {
        $this->Method1();
        $this->Method2();
        $this->Method3();
    }
}
 
class TemplateObject extends TemplateBase
{
}
 
class TemplateObject1 extends TemplateBase
{
    public function Method3()
    {
        echo "TemplateObject1 Method3<br/>";
    }
}
 
class TemplateObject2 extends TemplateBase
{
    public function Method2()
    {
        echo "TemplateObject2 Method2<br/>";
    }
}
 
// 实例化
$objTemplate = new TemplateObject();
$objTemplate1 = new TemplateObject1();
$objTemplate2 = new TemplateObject2();
 
$objTemplate->doSomeThing();
echo '<br />';
$objTemplate1->doSomeThing();
echo '<br />';
$objTemplate2->doSomeThing();
?>

十七、迭代模式

迭代器:类继承PHP的Iterator接口,批量操作。
1. 迭代器模式,在不需要了解内部实现的前提下,遍历一个聚合对象的内部元素。
2. 相比传统的编程模式,迭代器模式可以隐藏遍历元素的所需操作。
接口Iterator
current() 返回当前元素
key() 返回当前元素的键
next() 向前移动到下一个元素
rewind() 返回到迭代器的第一个元素

class AllUser implements \Iterator
{
    protected $index = 0;
    protected $data = [];

    public function __construct()
    {
        $link = mysqli_connect('192.168.0.91', 'root', '123', 'xxx');
        $rec = mysqli_query($link, 'select id from doc_admin');
        $this->data = mysqli_fetch_all($rec, MYSQLI_ASSOC);
    }

    //1 重置迭代器
    public function rewind()
    {
        $this->index = 0;
    }
xxx
    //2 验证迭代器是否有数据
    public function valid()
    {
        return $this->index < count($this->data);
    }

    //3 获取当前内容
    public function current()
    {
        $id = $this->data[$this->index];
        return User::find($id);
    }

    //4 移动key到下一个
    public function next()
    {
        return $this->index++;
    }


    //5 迭代器位置key
    public function key()
    {
        return $this->index;
    }
}

//实现迭代遍历用户表
$users = new AllUser();
//可实时修改
foreach ($users as $user){
    $user->add_time = time();
    $user->save();
}

十八、职责链模式

职责链模式(又叫责任链模式):
  包含了一些命令对象和一些处理对象,每个处理对象决定它能处理那些命令对象,它也知道应该把自己不能处理的命令对象交下一个处理对象,该模式还描述了往该链添加新的处理对象的方法。

角色:
  抽象处理者:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法,以设定和返回对下家的引用。这个角色通常由一个抽象类或接口实现。
  具体处理者:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。

适用场景:
  1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
  2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  3、可动态指定一组对象处理请求。

简单的总结责任链模式,可以归纳为:
  用一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合,唯一共同点是在他们之间传递request. 也就是说,来了一个请求,A类先处理,如果没有处理,就传递到B类处理,如果没有处理,就传递到C类处理,就这样象一个链条(chain)一样传递下去。

代码实现:

<?php
//申请类,
header("Content-type:text/html;Charset=utf-8");
class Request{
    public $name;
    public $requestContent;
    public $num;
    public $reason;
    function __construct($name,$requestContent,$reason,$num){
        $this->name = $name;
        $this->requestContent = $requestContent;
        $this->reason = $reason;
        $this->num = $num;
    }
}

//抽象处理类,定义处理者具有的公共属性和具体处理类需要实现的接口
abstract class AbstractManager{
    protected $name;   //姓名
    protected $position;  //职位
    protected $head;  //上司
    function __construct($name,$position){
        $this->name = $name;
        $this->position = $position;
    }
     //设置上司
    function setHead(AbstractManager $head){
        $this->head = $head;
    }
    //处理申请
    abstract function handleRequest(Request $request);
}

//具体处理类,经理,可以处理不多于7天的假期和不多于1000元的加薪请求
class Director extends AbstractManager{
    function __construct($name,$position){
        parent::__construct($name,$position);
    }    
    //处理申请
    function handleRequest(Request $request){
         if(($request->requestContent == "请假" && $request->num <7)||($request->requestContent == "加薪" && $request->num <1000) ){
              return $request->name."的".$request->requestContent."请求已被".$this->name."批准.";
         }else{  //超过处理权限转给上级总监处理
            if(isset($this->head)){
                return $this->head->handleRequest($request);
            }
         }
    }
}
//具体处理类,总监,可以处理不多于15天的假期和不多于2000元的加薪请求
class Majordomo extends AbstractManager{
    function __construct($name,$position){
        parent::__construct($name,$position);
    }    
    //处理申请
    function handleRequest(Request $request){
         if(($request->requestContent == "请假" && $request->num <15)||($request->requestContent == "加薪" && $request->num <2000) ){
              return $request->name."的".$request->requestContent."请求已被".$this->name."批准.";
         }else{  //超过处理权限转给上级总经理处理
            if(isset($this->head)){
                return $this->head->handleRequest($request);
            }
         }
    }
}
//具体处理类,总经理,可以处理请假和加薪请求
class GeneralManager extends AbstractManager{
    function __construct($name,$position){
        parent::__construct($name,$position);
    }    
    //处理申请
    function handleRequest(Request $request){
         if(($request->requestContent == "请假" && $request->num < 30)||($request->requestContent == "加薪" && $request->num <5000) ){
              return $request->name."的".$request->requestContent."请求已被".$this->name."批准.";
         }else{  //没有上级
            return $request->name."的".$request->requestContent."请求已被".$this->name."否决!";
         }
    }
}

//测试
$generalManagerWang = new GeneralManager("王五","总经理");
$majordomoLi = new Majordomo("李四","总监");
$majordomoLi->setHead($generalManagerWang);
$directZhang = new Director("张三","经理");
$directZhang->setHead($majordomoLi);

$request = new Request("叶良辰","请假","如果你有问题,可以随时找我,良辰就喜欢对自以为是的人出手",100);
$result = $directZhang->handleRequest($request);  //叶良辰的请假请求已被王五否决!
echo $result;

 十九、命令模式

命令模式 (Command Pattern): 将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

(一)为什么需要命令模式

1, 使用命令模式,能够让请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。

2,使用命令模式可以比较容易地设计一个命令队列和宏命令(组合命令),而且新的命令可以很容易地加入系统

 

(三)简单实例

有关命令模式的一个经典的例子,就是电视机与遥控器。如果按照命令模式的UML图那么,就有这样的对应关系:电视机是请求的接收者,遥控器是请求的发送者。遥控器上有一些按钮,不同的按钮对应电视机的不同操作。这些按钮就是对应的具体命令类。抽象命令角色由一个命令接口来扮演,有三个具体的命令类实现了抽象命令接口,这三个具体命令类分别代表三种操作:打开电视机、关闭电视机和切换频道。在实际使用中,命令模式的receiver经常是一个抽象类,就是对于不同的命令,它都有对应的具体命令接收者。命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开

<?php
//抽象命令角色
abstract class Command{
  protected $receiver;
  function __construct(TV $receiver)
  {
      $this->receiver = $receiver;
  }
  abstract public function execute();
}
//具体命令角色 开机命令
class CommandOn extends Command
{
  public function execute()
  {
      $this->receiver->action();
  }
}
//具体命令角色 关机机命令
class CommandOff extends Command
{
  public function execute()
  {
      $this->receiver->action();
  }
}
//命令发送者   --遥控器
class Invoker
{
  protected $command;
  public function setCommand(Command $command)
  {
      $this->command = $command;
  }

  public function send()
  {
      $this->command->execute();
  }
}
//命令接收者 Receiver =》 TV
class TV
{
  public function action()
  {
      echo "接收到命令,执行成功".PHP_EOL;
  }
}

//实例化命令接收者 -------买一个电视机
$receiver = new TV();
//实例化命令发送者-------配一个遥控器
$invoker  = new Invoker();
//实例化具体命令角色 -------设置遥控器按键匹配电视机
$commandOn = new CommandOn($receiver);
$commandOff = new CommandOff($receiver);
//设置命令  ----------按下开机按钮
$invoker->setCommand($commandOn);
//发送命令
$invoker->send();
//设置命令  -----------按下关机按钮
$invoker->setCommand($commandOff);
//发送命令
$invoker->send();

作者:刀斧手何在
链接:https://www.imooc.com/article/17844
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作

二十、备忘录模式

又叫做快照模式或Token模式,在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

角色:
  1.创建者:负责创建一个备忘录,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。发起人可以根据需要决定备忘录存储自己的哪些内部状态。
  2.备忘录:负责存储发起人对象的内部状态,并可以防止发起人以外的其他对象访问备忘录。备忘录有两个接口:管理者只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。发起人却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
  3.管理者:负责存取备忘录,不能对的内容进行访问或者操作。

适用性:
  必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

<?php
header("Content-type:text/html;Charset=utf-8");

//备忘录
class Memento{
    public $id;
    public $name;
    public $liveLevel;

    function __construct($id,$name,$liveLevel){
        $this->id = $id;
        $this->name = $name;
        $this->liveLevel = $liveLevel;
    }
}

//备忘录管理器
class Originator{
     public static $mementos = array();
     private static $instance = null;

     //单例模式确保只有一个管理器
     private function __construct(){

     }
     //返回单例对象
     static function getInstance(){
         if(!(self::$instance instanceOf slef)){
              self::$instance = new self();
         }
         return self::$instance;
     }
     //存备忘录
     function setMemento($id,Memento $memento){
         self::$mementos[$id] = $memento;
     }
     //取备忘录
     function getMemento($id){
         return self::$mementos[$id];
     }
}

//创建者,玩家,可存取自身状态
class Player{
    private static $i = 0;  //静态变量累加用于给$id赋值
    public $id; //每个对象独一无二,用于保存状态备忘录到管理器
    private $name; //姓名
    private $liveLevel;  //生命值

    function __construct($name){
        $this->name = $name;
        $this->id = self::$i;
        self::$i++;
    }
    //初始化
    function init(){
       $this->liveLevel = 100;
    }

    //生命值减少10
    function damage(){
        $this->liveLevel -=10;
    }
   
    //显示现有状态
    function displayState(){
        echo "姓  名:".$this->name."<br/>";
        echo "生命值:".$this->liveLevel."<br/>";
    }

    //保存状态到一个备忘录中,该备忘录将被放置到管理器中
    function saveState(){
        $originator = Originator::getInstance();
        $originator->setMemento($this->id,new Memento($this->id,$this->name,$this->liveLevel));
    }
    //恢复备忘录
    function getState(){
        $originator = Originator::getInstance();
        $memento = $originator->getMemento($this->id);
        $this->id = $memento->id;
        $this->name = $memento->name;
        $this->liveLevel = $memento->liveLevel;
    }
}

//测试
//创建、初始化角色并显示状态
$player = new Player("张三");
$player->init();
$player->displayState(); //生命值100

//开始游戏前存档
$player->saveState();

//开始游戏,收到伤害生命值减少,显示状态
$player->damage();
$player->damage();
$player->displayState(); //生命值80

//回档恢复原来状态,显示状态
$player->getState();
$player->displayState();  //生命值100

 ?>

二十一、状态模式

        在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。 对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

<?php


interface State
{
    public function doAction(Context $context);
}

//(带有某个状态的类)
class Context
{
    private $state;

    public function __construct()
    {
        $this->state = null;
    }

    public function setState(State $state)
    {
        $this->state = $state;
    }

    public function getState()
    {
        return $this->state;
    }
}

//(具体的开始状态类)
class Context
{
    private $state;

    public function __construct()
    {
        $this->state = null;
    }

    public function setState(State $state)
    {
        $this->state = $state;
    }

    public function getState()
    {
        return $this->state;
    }
}

//(具体的结束状态类)

class StopState implements State
{
    public function doAction(Context $context)
    {
        echo "Player is in stop state";
        $context->setState($this);
    }


    public function handle()
    {
        return 'stop state';
    }
}

//(客户端类)
spl_autoload_register(function ($className){
    $className = str_replace('\\','/',$className);
    include $className.".class.php";
});

use State\Context;
use State\StartState;
use State\StopState;

$context = new  Context();

$startState = new StartState();
$startState->doAction($context);
$context->getState()->handle();

$startState = new StopState();
$startState->doAction($context);
$context->getState()->handle();

 二十二、访问者模式

<?php 
/**
 * 访问者模式
 * 封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
 * 行为类模式 
 */
 
 
/**
抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。
 */
   
interface Visitor { 
    public function visitConcreteElementA(ConcreteElementA $elementA); 
    public function visitConcreteElementB(concreteElementB $elementB); 
} 
   
interface Element { 
    public function accept(Visitor $visitor); 
} 
   
/**
 * 具体的访问者1
 */ 
class ConcreteVisitor1 implements Visitor { 
    public function visitConcreteElementA(ConcreteElementA $elementA){ 
        echo $elementA->getName(),' visitd by ConcerteVisitor1 <br />'; 
    } 
   
    public function visitConcreteElementB(ConcreteElementB $elementB){ 
        echo $elementB->getName().' visited by ConcerteVisitor1 <br />'; 
    } 
   
} 
   
/**
 * 具体的访问者2
 */ 
class ConcreteVisitor2 implements Visitor { 
    public function visitConcreteElementA(ConcreteElementA $elementA){ 
        echo $elementA->getName(),   ' visitd by ConcerteVisitor2 <br />'; 
    } 
   
    public function visitConcreteElementB(ConcreteElementB $elementB){ 
        echo $elementB->getName(), ' visited by ConcerteVisitor2 <br />'; 
    } 
   
} 
   
/**
 * 具体元素A
 */ 
class ConcreteElementA implements Element { 
    private$_name; 
   
    public function __construct($name){ 
        $this->_name =$name; 
    } 
   
    public function getName(){ 
        return$this->_name; 
    } 
   
    /**
     * 接受访问者调用它针对该元素的新方法
     * @param Visitor $visitor
     */ 
    public function accept(Visitor $visitor){ 
        $visitor->visitConcreteElementA($this); 
    } 
   
} 
   
/**
 *  具体元素B
 */ 
class ConcreteElementB implements Element { 
    private$_name; 
   
    public function __construct($name){ 
        $this->_name =$name; 
    } 
   
    public function getName(){ 
        return$this->_name; 
    } 
   
    /**
     * 接受访问者调用它针对该元素的新方法
     * @param Visitor $visitor
     */ 
    public function accept(Visitor $visitor){ 
        $visitor->visitConcreteElementB($this); 
    } 
   
} 
   
/**
 * 对象结构 即元素的集合
 */ 
class ObjectStructure { 
    private$_collection; 
   
    public function __construct(){ 
        $this->_collection =array(); 
    } 
   
   
    public function attach(Element $element){ 
        return array_push($this->_collection,$element); 
    } 
   
    public function detach(Element $element){ 
        $index=array_search($element,$this->_collection); 
        if($index!==FALSE){ 
            unset($this->_collection[$index]); 
        } 
   
        return$index; 
    } 
   
    public function accept(Visitor $visitor){ 
        foreach($this->_collection as $element){ 
            $element->accept($visitor); 
        } 
    } 
} 
   
class Client { 
   
    /**
     * Main program.
     */ 
    public static function main(){ 
        $elementA = new ConcreteElementA("ElementA"); 
        $elementB = new ConcreteElementB("ElementB"); 
        $elementA2 = new ConcreteElementB("ElementA2"); 
        $visitor1 = new ConcreteVisitor1(); 
        $visitor2 = new ConcreteVisitor2(); 
   
        $os = new ObjectStructure(); 
        $os->attach($elementA); 
        $os->attach($elementB); 
        $os->attach($elementA2); 
        $os->detach($elementA); 
        $os->accept($visitor1); 
        $os->accept($visitor2); 
    } 
   
} 
   
Client::main(); 
?> 

二十三、中介模式

<?php
/*
 * 中介者模式:用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用从而使其耦合松散,而且可以独立地改变它们之间的交互
 */
 
/*
 * 以一个同学qq群为例说明,qq作为中介者,同学作为相互交互的对象
 */
 
//抽象中介者,利用中介发送消息
abstract class Mediator{
    abstract function send($message,$user);
}
 
//抽象同事类,利用中介发送消息
abstract class Colleague{
    private $mediator;
     
    public function __construct($mediator){
        $this->mediator = $mediator;
    }
     
    public function send($message){
        $this->mediator->send($message,$this);
    }
     
    abstract function notify($message);
}
 
//具体的同事类a b c d e
class StuA extends Colleague{
    public function notify($message){
        echo 'Stu A says: '.$message;
        echo "<br/>";
    }
}
 
class StuB extends Colleague{
    public function notify($message){
        echo 'Stu B says: '.$message;
        echo "<br/>";
    }
}
 
class StuC extends Colleague{
    public function notify($message){
        echo 'Stu C says: '.$message;
        echo "<br/>";
    }
}
 
class StuD extends Colleague{
    public function notify($message){
        echo 'Stu D says: '.$message;
        echo "<br/>";
    }
}
 
class StuE extends Colleague{
    public function notify($message){
        echo 'Stu E says: '.$message;
        echo "<br/>";
    }
}
 
//具体的中介者
class QQ extends Mediator{
    public $users = array();
     
    public function setUsers($user){//把对象添加进来
        $this->users[] = $user;
    }
     
    public function send($message, $user){//推送消息
        for($i=0;$i<count($this->users);$i++){
            if($user == $this->users[$i]){
                $this->users[$i]->notify($message);
            }
        }
    }
}
 
class Client{
    public static function main(){
        //流程:先建立中介者
        $qq = new QQ();
        //实例化交互对象
        $stu_a = new StuA($qq);
        $stu_b = new StuB($qq);
        $stu_c = new StuC($qq);
        $stu_d = new StuD($qq);
        $stu_e = new StuE($qq);
        //把对象加入中介者
        $qq->setUsers($stu_b);
        $qq->setUsers($stu_a);
        $qq->setUsers($stu_c);
        $qq->setUsers($stu_d);
        $qq->setUsers($stu_e);
         
        //交流
        $stu_a->send('昨天点名了吗,各位大神');
        $stu_c->send('难道你没去');
        $stu_a->send('是啊');
        $stu_b->send('恭喜你中奖了');
        $stu_d->send('恭喜你中奖了');
        $stu_e->send('恭喜你中奖了');
        $stu_a->send('我去');
    }
}
 
Client::main();
?>

二十四、解释器模式

定义:

解释器模式(interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。解释器模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

结构:

  • AbstractExpression(抽象表达式):表明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
  • TerminalExpression(终结符表达式):实现与文法中的终结符相关联的解释操作,实现抽象表达式中所要求的接口,文法中每一个终结符都有一个具体终结表达式与之相对应。
  • NonterminalExpression(非终结符表达式):为文法中的非终结符实现解释操作。
  • Context:包含解释器之外的一些全局信息。
  • Client:客户端代码,构建表示该文法定义的语言中一个特定的句子的抽象语法树,调用解释操作。
  • 代码实例:用解释器模式设计一个“韶粵通”公交车卡的读卡器程序。说明:假如“韶粵通”公交车读卡器可以判断乘客的身份,如果是“韶关”或者“广州”的“老人” “妇女”“儿童”就可以免费乘车,其他人员乘车一次扣 2 元。
  • 
    // Expression.php
    /**
     * 抽象表达类
     * Class Expression
     */
    abstract class Expression
    {
        abstract public function interpret($info);
    }
    
    
    /**
     * 终结符表达式类
     * Class TerminalExpression
     */
    class TerminalExpression extends Expression
    {
        private $value = [];
    
        public function __construct(array $value)
        {
            $this->value = $value;
        }
    
        public function interpret($info)
        {
            // TODO: Implement interpret() method.
            if (in_array($info, $this->value)) {
                return true;
            } else {
                return false;
            }
        }
    }
    
    
    /**
     * 非终结符表达式类
     * Class AndExpression
     */
    class AndExpression extends Expression
    {
        private $city;
        private $person;
    
        public function __construct(Expression $city, Expression $person)
        {
            $this->city = $city;
            $this->person = $person;
        }
    
        public function interpret($info)
        {
            // TODO: Implement interpret() method.
            $data = explode('的', $info);
            return $this->city->interpret($data[0]) && $this->person->interpret($data[1]);
        }
    }
    
    
    /**
     * Class Context
     */
    class Context
    {
        private $city = ["韶关", "广州"];
        private $person = ["老人", "妇女", "儿童"];
        private $cityPerson;
    
        public function __construct()
        {
            $cityExpression = new TerminalExpression($this->city);
            $personExpression = new TerminalExpression($this->person);
            $this->cityPerson = new AndExpression($cityExpression, $personExpression);
        }
    
        public function freeRide($info)
        {
            $isTrue = $this->cityPerson->interpret($info);
            if ($isTrue) {
                echo "您是" . $info . ",您本次乘车免费!\n";
            } else {
                echo $info . ",您不是免费人员,本次乘车扣费2元!\n";
            }
        }
    }
     
    客户端调用:
    
    复制代码
    $context = new Context();
    $context->freeRide("韶关的老人");
    $context->freeRide("韶关的年轻人");
    $context->freeRide("广州的妇女");
    $context->freeRide("广州的儿童");
    $context->freeRide("山东的儿童");
    
    
    结果:
    
    您是韶关的老人,您本次乘车免费!
    韶关的年轻人,您不是免费人员,本次乘车扣费2元!
    您是广州的妇女,您本次乘车免费!
    您是广州的儿童,您本次乘车免费!
    山东的儿童,您不是免费人员,本次乘车扣费2元!
     
    
    总结:
    通常当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
    使用解释器模式,意味着可以很容易的改变和扩展文法,因为该模式使用类来表示文法规则,你可以使用继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。
    解释器模式也有不足的地方,解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值