美文网首页PHP
php设计模式

php设计模式

作者: DragonRat | 来源:发表于2018-07-20 17:52 被阅读0次

    写在前面

    本文摘自:
    https://laravel-china.org/docs/php-design-patterns/2018/Bridge/1497
    http://larabase.com/collection/5/post/152
    https://code-ken.github.io/2016/03/01/DesignPatternWithPHP-9/
    https://blog.csdn.net/jhq0113/article/details/45441793
    https://www.kancloud.cn/martist/ma_zhao_liu/377825

    设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    分类

    1. 创建型
      在软件工程中,创建型设计模式是处理对象创建机制的设计模式,试图以适当的方式来创建对象。对象创建的基本形式可能会带来设计问题,亦或增加了设计的复杂度。创建型设计模式通过控制这个对象的创建方式来解决此问题
    2. 结构型
      在软件工程中,结构型设计模式是通过识别实体之间关系来简化设计的设计模式
    3. 行为型
      在软件工程中,行为设计模式是识别对象之间的通用通信模式并实现这些模式的设计模式。 通过这样做,这些模式增加了执行此通信的灵活性

    创建型

    简单工厂模式(Simple Factory)

    工厂模式:
    不用new来获得实例,而是把业务类放进一个工场类里,由工厂(类)『生产』出对应的实例

    简单工厂模式实现:
    工厂类必须有一个工厂方法;
    工厂方法必须能够返回一个其他类的实例;
    一次只能创建和返回一个实例;

    代码实现
    SimpleFactory.php

    <?php
    namespace DesignPatterns\Creational\SimpleFactory;
    class SimpleFactory
    {
        public function createBicycle(): Bicycle
        {
            return new Bicycle();
        }
    }
    

    Bicycle.php

    <?php
    namespace DesignPatterns\Creational\SimpleFactory;
    class Bicycle
    {
        public function driveTo(string $destination)
        {
        }
    }
    

    使用

    $factory = new SimpleFactory();
     $bicycle = $factory->createBicycle();
     $bicycle->driveTo('Paris');
    

    工厂方法模式

    概念
    工厂方法模式 和 简单工厂模式非常接近,唯一不同的是,允许有多个工厂存在,相当于给工厂分组。

    规则
    1、每个工厂必须继承一个抽象类或接口类, 使之成为多态。
    2、每个产品也必须继承一个抽象类或接口类,也使之成为多态。
    3、每个工厂必须有一个工厂方法返回产品的实例

    实现

    /* 工厂和产品接口 */
    interface CarFactory 
    {
        public function makeCar();
    }
    interface Car 
    {
        public function getType();
    }
    /* 工厂和产品实现 */
    class SedanFactory implements CarFactory 
    {
        public function makeCar() 
        {
            return new Sedan();
        }
    }
    class Sedan implements Car 
    {
        public function getType() 
        {
            return 'Sedan';
        }
    }
    /* 客户端 */
    $factory = new SedanFactory();
    $car = $factory->makeCar();
    print $car->getType();
    

    抽象工厂模式

    区别
    抽象工厂模式与工厂方法模式在某种程度上是一样的,区别在于子工厂必须全部继承或实现自同一个抽象类或接口。

    规则

    1. 每个工厂必须继承同一个抽象类或实现同一个接口。
    2. 每个工厂必须包含多个工厂方法。
    3. 每个工厂的方法必须一致,每个方法发返回的实例,必须是继承或实现了同一个抽象类或接口的多态类
    <?php
    abstract class Button{}
    abstract class Border{}
    class MacButton extends Button{}
    class WinButton extends Button{}
    class MacBorder extends Border{}
    class WinBorder extends Border{}
    interface AbstractFactory {
        public function CreateButton();
        public function CreateBorder();
    }
    class MacFactory implements AbstractFactory{
        public function CreateButton(){ return new MacButton(); }
        public function CreateBorder(){ return new MacBorder(); }
    }
    class WinFactory implements AbstractFactory{
        public function CreateButton(){ return new WinButton(); }
        public function CreateBorder(){ return new WinBorder(); }
    }
    

    抽象工厂一般使用接口,特点是层层约束的,它会让你的代码非常一致,不会被别人写乱。

    静态工厂模式

    “静态工厂模式”与“抽象工厂模式”的区别在于,只使用一个静态方法来创建所有类型对象,此方法通常被为命名factory或build

    <?php
    
    namespace DesignPatterns\Creational\StaticFactory;
    
    /**
     * 注意点1: 记住,静态意味着全局状态,因为它不能被模拟进行测试,所以它是有弊端的
     * 注意点2: 不能被分类或模拟或有多个不同的实例。
     */
    final class StaticFactory
    {
        /**
        * @param string $type
        *
        * @return FormatterInterface
        */
        public static function factory(string $type): FormatterInterface
        {
            if ($type == 'number') {
                return new FormatNumber();
            }
    
            if ($type == 'string') {
                return new FormatString();
            }
    
            throw new \InvalidArgumentException('Unknown format given');
        }
    }
    

    单例模式(Singleton):

    目的:
    在应用程序调用的时候,只能获得一个对象实例。
    例子:
    数据库连接;日志 (多种不同用途的日志也可能会成为多例模式);在应用中锁定文件 (系统中只存在一个 ...)
    单利模式的实现:
    三私一公
    ①私有化构造方法:防止实例化
    ②私有化克隆方法:防止克隆
    ③私有化静态属性:保存对象
    ④私有化静态方法:获取对象

    //代码实现
    <?php
        class Mysql{
            //该属性用来保存实例
            private static $conn;
            //构造函数为private,防止创建对象
            private function __construct(){
                self::$conn = mysqli_connect('localhost','root','');
            }
            //创建一个用来实例化对象的方法,如果不存在一个这个类的实例属性,就创建一个,否则就取这个实例属性。
            public static function getInstance(){
                if(!(self::$conn instanceof self)){
                    self::$conn = new self;
                }
                return self::$conn;
            }
            //防止对象被复制
            public function __clone(){
                trigger_error('Clone is not allowed !');
            }
            //防止反序列化后创建对象
            private function __wakeup(){
                trigger_error('Unserialized is not allowed !');
            }
        }
        //只能这样取得实例,不能new 和 clone
        $mysql = Mysql::getInstance();
    ?>
    

    对象池模式(Objects Pool)

    单例模式的一种升级模式;与单例模式的区别在于,相当于一个对象池 管理多个单例

    实现

    class ObjectPool
    {
        private $instances = [];
        public function get($key)
        {
            if(isset($this->instances[$key])){
                return $this->instances[$key];
            }else{
               $item = $this->make($key);
               $this->instances[$key]=$item;
               return $item;
            }
        }
         public function add($object, $key)
         {
             $this->instances[$key] = $object;
         }
         public function make($key){
             if($key =='mysql'){
                return new Mysql();
            }elseif($key =='socket'){
                return new Socket();
            }
         }
    }
    class ReusableObject
    {
        public function doSomething()
        {
            // ...
        }
    }
    

    使用

    $pool = new ObjectPool();
    $pool->get('mysql');
    

    如果对象池里有mysql的对象实例,就拿出来,如果没有就新建一个,这样无论怎样mysql的实例只会被创建一次,并且会保存在内存中,以便复用。

    多例模式

    多例类可以有多个实例;多例类必须自己创建、管理自己的实例,并向外界提供自己的实例(1. 通过实例容器保存容器。2. 利用私有构造阻止外部构造。3. 提供getInstantce()方法获取实例)

    代码

    //机器人
    public class Droid {
      //机器人容器,生产出来的机器人都放在这里面
      private static HashMap<String, Droid> droids = new HashMap<>();
    
      //为机器人选了两个名字,一个叫“阿尔法狗”,另一个叫“骂死他”        
      public static final String[] names = {"阿尔法狗", "骂死他"};
    
      //机器人应该有自己名字
      private String name;
    
      //直接制造两个机器人,放入机器人容器
      static{
        //第一个机器人是阿尔法狗
        Droid alphaGo = new Droid(names[0]);
        droids.put(names[0], alphaGo);
    
        //第二个机器人是骂死他
        Droid master = new Droid(names[1]);
        droids.put(names[1], master);
      }
    
      //构造机器人的时候,应该取好名字
      //私有构造函数,在外部无法进行构造
      private Droid(String name){
        this.name = name;
      }
    
      //随便抓一个机器人,无论是谁,是机器人就行。
      public static Droid getRandomInstance(){
        Random random = new Random();
        int index = random.nextInt(2);
        String name = names[index];
        return droids.get(name);
      }
    
      //叫机器人的名字,找到和名字匹配的那个机器人
      public static Droid getInstance(String name){
        return droids.get(name);
      }
    
      //机器人有一个应答功能,会报告自己名字。
      public void reply(){
        System.out.println("I am " + name + ", I'm here.");
      }
    }
    
    
    //对机器人进行点名
    public class Call {
      public static void main(String[] args) {
        //随便抓机器人,抓6次,每抓一个机器人出来,就让它报自己的名字
        for(int i=0; i<6; i++){
          Droid droid = Droid.getRandomInstance();
          droid.reply();
        }
    
        System.out.println("");
        System.out.println("=====华丽丽的分隔符=====");
        System.out.println("");
    
        //找到阿尔法狗,让它报道
        Droid alphaGo = Droid.getInstance(Droid.names[0]);
        alphaGo.reply();
    
        //找到骂死他,让它报道
        Droid master = Droid.getInstance(Droid.names[1]);
        master.reply();
      }
    }
    

    原型模式(Prototype)

    对一些大型对象,每次去new,初始化开销很大,这个时候我们 先new 一个模版对象,然后其他实例都去clone这个模版, 这样可以节约不少性能

    <?php
    interface Prototype { public function copy(); }
    class ConcretePrototype implements Prototype{
        private  $_name;
        public function __construct($name) { $this->_name = $name; } 
        public function copy() { return clone $this;}
    }
    class Demo {}
    // client
    $demo = new Demo();
    $object1 = new ConcretePrototype($demo);
    $object2 = $object1->copy();
    ?>
    

    把需要被复制的对象丢进 原型类里面,然后这个类就具有了 复制自己的能力(方法),并且可以继承原型的一些公共的属性和方法

    建造者模式

    建造者模式也称生成器模式,核心思想是将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。

    建造者模式一般认为有四个角色:

     1.产品角色,产品角色定义自身的组成属性
     2.抽象建造者,抽象建造者定义了产品的创建过程以及如何返回一个产品
     3.具体建造者,具体建造者实现了抽象建造者创建产品过程的方法,给产品的具体属性进行赋值定义
     4.指挥者,指挥者负责与调用客户端交互,决定创建什么样的产品
    
    <?php
    /**
     * Created by PhpStorm.
     * User: Jiang
     * Date: 2015/4/25
     * Time: 9:31
     */
     
    /**具体产品角色  鸟类
     * Class Bird
     */
    class Bird
    {
        public $_head;
        public $_wing;
        public $_foot;
     
        function show()
        {
            echo "头的颜色:{$this->_head}<br/>";
            echo "翅膀的颜色:{$this->_wing}<br/>";
            echo "脚的颜色:{$this->_foot}<br/>";
        }
    }
     
    /**抽象鸟的建造者(生成器)
     * Class BirdBuilder
     */
    abstract class BirdBuilder
    {
        protected $_bird;
     
        function __construct()
        {
            $this->_bird=new Bird();
        }
     
        abstract function BuildHead();
        abstract function BuildWing();
        abstract function BuildFoot();
        abstract function GetBird();
    }
     
    /**具体鸟的建造者(生成器)   蓝鸟
     * Class BlueBird
     */
    class BlueBird extends BirdBuilder
    {
     
        function BuildHead()
        {
            // TODO: Implement BuilderHead() method.
            $this->_bird->_head="Blue";
        }
     
        function BuildWing()
        {
            // TODO: Implement BuilderWing() method.
            $this->_bird->_wing="Blue";
        }
     
        function BuildFoot()
        {
            // TODO: Implement BuilderFoot() method.
            $this->_bird->_foot="Blue";
        }
     
        function GetBird()
        {
            // TODO: Implement GetBird() method.
            return $this->_bird;
        }
    }
     
    /**玫瑰鸟
     * Class RoseBird
     */
    class RoseBird extends BirdBuilder
    {
     
        function BuildHead()
        {
            // TODO: Implement BuildHead() method.
            $this->_bird->_head="Red";
        }
     
        function BuildWing()
        {
            // TODO: Implement BuildWing() method.
            $this->_bird->_wing="Black";
        }
     
        function BuildFoot()
        {
            // TODO: Implement BuildFoot() method.
            $this->_bird->_foot="Green";
        }
     
        function GetBird()
        {
            // TODO: Implement GetBird() method.
            return $this->_bird;
        }
    }
     
    /**指挥者
     * Class Director
     */
    class Director
    {
        /**
         * @param $_builder      建造者
         * @return mixed         产品类:鸟
         */
        function Construct($_builder)
        {
            $_builder->BuildHead();
            $_builder->BuildWing();
            $_builder->BuildFoot();
            return $_builder->GetBird();
        }
    
    

    客户端

    header("Content-Type:text/html;charset=utf-8");
    //------------------------生成器模式测试代码------------------
    require_once "./Builder/Builder.php";
     
    $director=new Director();
     
    echo "蓝鸟的组成:<hr/>";
     
    $blue_bird=$director->Construct(new BlueBird());
    $blue_bird->Show();
     
    echo "<br/>Rose鸟的组成:<hr/>";
     
    $rose_bird=$director->Construct(new RoseBird());
    $rose_bird->Show();
    

    结构型

    适配器模式(适配器)

    将一个类的接口转换成可应用的兼容接口。适配器使原本由于接口不兼容而不能一起工作的那些类可以一起工作(适配器就是不论接受的输入是什么,它的输出总是一定的)

    代码

    
    <?php
    /**
    * 对象适配器模式的PHP简单实现 2010-07-10 sz
    * @author 胖子 phppan.p#gmail.com http://www.phppan.com
    * 哥学社成员(http://www.blog-brother.com/)
    * @package design pattern
    */
     
    /**
    * 目标角色
    */
    interface Target {
     
    /**
    * 源类也有的方法1
    */
    public function sampleMethod1();
     
    /**
    * 源类没有的方法2
    */
    public function sampleMethod2();
    }
     
    /**
    * 源角色
    */
    class Adaptee {
     
    /**
    * 源类含有的方法
    */
    public function sampleMethod1() {
    echo 'Adaptee sampleMethod1 <br />';
    }
    }
     
    /**
    * 类适配器角色
    */
    class Adapter implements Target {
     
    private $_adaptee;
     
    public function __construct(Adaptee $adaptee) {
    $this->_adaptee = $adaptee;
    }
     
    /**
    * 委派调用Adaptee的sampleMethod1方法
    */
    public function sampleMethod1() {
    $this->_adaptee->sampleMethod1();
    }
     
    /**
    * 源类中没有sampleMethod2方法,在此补充
    */
    public function sampleMethod2() {
    echo 'Adapter sampleMethod2 <br />';
    }
     
    }
     
    class Client {
     
    /**
    * Main program.
    */
    public static function main() {
    $adaptee = new Adaptee();
    $adapter = new Adapter($adaptee);
    $adapter->sampleMethod1();
    $adapter->sampleMethod2();
     
    }
     
    }
     
    Client::main();
    
    

    桥接模式

    将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化(我们知道一个类可以实现多个接口,一个接口对应多个实现。在不同的实现类中,它实现接口方法的逻辑是不一样的。有时候我们需要对这些抽象方法进行一些组合,修改,但是又能适用于所有实现类。这时候我们需要做一个桥,连接不同的实现类并统一标准;)

    <?php
    namespace Bridge;
    
    abstract class info
    {
        protected $send = null;
    
        public function __construct($send)
        {
            $this->send = $send;
        }
    
        abstract public function msg($content);
    
        public function send($to, $content)
        {
            $content = $this->msg($content);
            $this->send->send($to, $content);
        }
    }
    
    class email
    {
        public function send($to, $content)
        {
            echo 'email给' . $to . '的内容是' . $content;
        }
    }
    
    class sns
    {
        public function send($to, $content)
        {
            echo 'sns给' . $to . '的内容是' . $content;
        }
    }
    
    
    
    class commonInfo extends info
    {
        public function msg($content)
        {
            return '普通' . $content;
        }
    }
    
    class warnInfo extends info
    {
        public function msg($content)
        {
            return '紧急' . $content;
        }
    }
    
    //====客户端====
    header("Content-type:text/html;charset = utf-8");
    
    $send = new warnInfo(new email());
    $send->send("小明","内容");
    

    组合模式

    组合模式(有时候又叫做部分-整体模式),将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

    三大角色
    抽象结构(Company)角色:此角色实现所有类共有接口的默认行为,声明一个接口管理子部件。
    叶子节点(Technology)角色:表示叶子对象,没有子节点。
    枝节点(SubCompany)角色:用来存储子部件,实现与子部件有关的操作,如Add()等。

    代码

    
    <?php
    /**
     * Created by PhpStorm.
     * User: Jiang
     * Date: 2015/5/2
     * Time: 21:14
     */
     
    /**抽象结构角色          公司
     * Class Company
     */
    abstract class Company
    {
        protected $name;
     
        function __construct($name)
        {
            $this->name=$name;
        }
     
        /**增加
         * @param Company $company    子公司,部门
         * @return mixed
         */
        abstract function Add(Company $company);
     
        /**移除
         * @param Company $company   子公司,部门
         * @return mixed
         */
        abstract function Remove(Company $company);
     
        /**显示公司及部门结构
         * @param $depth
         * @return mixed
         */
        abstract function Display($depth);
     
    }
     
    /**枝节点               子公司
     * Class Beijing
     */
    class SubCompany extends Company
    {
     
        private $sub_companys=array();
     
        function __construct($name)
        {
            parent::__construct($name);
        }
     
        function Add(Company $company)
        {
            $this->sub_companys[]=$company;
        }
     
        function Remove(Company $company)
        {
            $key=array_search($company,$this->sub_companys);
            if($key!==false)
            {
                unset($this->sub_companys[$key]);
            }
        }
     
        function Display($depth)
        {
            $pre="";
            for($i=0;$i<$depth;$i++)
            {
                $pre.="-";
            }
            $pre.=$this->name."<br/>";
            echo $pre;
     
            foreach($this->sub_companys as $v)
            {
                $v->Display($depth+2);
            }
        }
     
    }
     
    /**叶子节点                    财务部
     * Class DeptCompany
     */
    class MoneyDept extends Company
    {
     
        function __construct($name)
        {
            parent::__construct($name);
        }
     
        /**增加
         * @param Company $company 子公司,部门
         * @return mixed
         */
        function Add(Company $company)
        {
            echo "叶子节点,不能继续添加节点。。。。。。。。。。<br/>";
        }
     
        /**移除
         * @param Company $company 子公司,部门
         * @return mixed
         */
        function Remove(Company $company)
        {
            echo "叶子节点,不能删除节点。。。。。。。。。。<br/>";
        }
     
        /**显示公司及部门结构
         * @param $depth
         * @return mixed
         */
        function Display($depth)
        {
            $pre="";
            for($i=0;$i<$depth;$i++)
            {
                $pre.="-";
            }
            $pre.=$this->name."<br/>";
            echo $pre;
        }
     
    }
     
    /**叶子节点                    技术部门
     * Class DeptCompany
     */
    class TechnologyDept extends Company
    {
     
        function __construct($name)
        {
            parent::__construct($name);
        }
     
        /**增加
         * @param Company $company 子公司,部门
         * @return mixed
         */
        function Add(Company $company)
        {
            echo "叶子节点,不能继续添加节点。。。。。。。。。。<br/>";
        }
     
        /**移除
         * @param Company $company 子公司,部门
         * @return mixed
         */
        function Remove(Company $company)
        {
            echo "叶子节点,不能删除节点。。。。。。。。。。<br/>";
        }
     
        /**显示公司及部门结构
         * @param $depth
         * @return mixed
         */
        function Display($depth)
        {
            $pre="";
            for($i=0;$i<$depth;$i++)
            {
                $pre.="-";
            }
            $pre.=$this->name."<br/>";
            echo $pre;
        }
     
    }
    

    客户端

    header("Content-Type:text/html;charset=utf-8");
    //------------------------组合器模式测试代码------------------
    require_once "./Composer/Composer.php";
     
    $root=new SubCompany("北京总公司");
    $root->Add(new MoneyDept("总公司财务部"));
    $root->Add(new TechnologyDept("总公司技术部"));
     
    $shanghai=new SubCompany("上海分公司");
    $shanghai->Add(new TechnologyDept("上海分公司技术部"));
    $shanghai->Add(new MoneyDept("上海分公司财务部"));
     
    $root->Add($shanghai);
     
    $root->Display(1);
     
    echo "<hr/>";
     
    $root->Remove($shanghai);
    $root->Display(3);
    

    数据映射模式

    描述如何创建提供透明访问任何数据源的对象。数据映射模式,也叫数据访问对象模式,或数据对象映射模式

    实例:ORM实现

    代码

    <?php
    //数据模式映射类
    class User
    {
        protected $id;
        protected $data;
        protected $db;
        protected $change = false;
    
        public function __construct($id)
        {   
            $this->id = $id;
            //实例化数据库对象,这里使用了工厂方法
            $this->db = Factory::getDatabase();
            //从数据库查询数据,存放到data属性中
            $this->data  = $this->db->query("select * from user where id = $id limit 1");
    
        }
    
        public function __get($key)
        {
            if (isset($this->data[$key]))
            {
                return $this->data[$key];
            }
        }
    
        public function __set($key, $value)
        {
            $this->data[$key] = $value;
            $this->change = true;
        }
        //析构方法
        public function __destruct()
        {
            //如果对象属性改变过,则change属性为true 则调更新方法更新数据库
           $this->change && $this->update();
        }
        //更新记录方法
        public function update(){
             foreach ($this->data as $k => $v)
                {
                    $fields[] = "$k = '{$v}'";
                }
                $this->db->query("update user set " . implode(', ', $fields) . "where
                id = {$this->id} limit 1");
        }
    }
    //实例化对象
    $user = new User(1);
    //改变名字
    $user->name = 'admin';
    

    装饰器模式(Decorator Pattern)

    动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

    简单实例

    <?php
    /*游戏原来的角色类
    class Person{
        public function clothes(){
            echo "长衫".PHP_EOL;
        }
    }
    */
    
    //装饰器接口
    interface Decorator
    {
       public function beforeDraw();
       public function afterDraw();
    }
    //具体装饰器1-宇航员装饰
    class AstronautDecorator implements Decorator
    {
        public function beforeDraw()
        {
            echo "穿上T恤".PHP_EOL;
        }
        function afterDraw()
        {
            echo "穿上宇航服".PHP_EOL;
            echo "穿戴完毕".PHP_EOL;
        }
    }
    //具体装饰器2-警察装饰
    class PoliceDecorator implements Decorator{
        public function beforeDraw()
        {
            echo "穿上警服".PHP_EOL;
        }
        function afterDraw()
        {
            echo "穿上防弹衣".PHP_EOL;
            echo "穿戴完毕".PHP_EOL;
        }
    }
    //被装饰者
    class Person{
        protected $decorators = array(); //存放装饰器
        //添加装饰器
        public function addDecorator(Decorator $decorator)
        {
            $this->decorators[] = $decorator;
        }
        //所有装饰器的穿长衫前方法调用
        public function beforeDraw()
        {
            foreach($this->decorators as $decorator)
            {
                $decorator->beforeDraw();
            }
        }
        //所有装饰器的穿长衫后方法调用
        public function afterDraw()
        {
            $decorators = array_reverse($this->decorators);
            foreach($decorators as $decorator)
            {
                $decorator->afterDraw();
            }
        }
        //装饰方法
        public function clothes(){
            $this->beforeDraw();
            echo "穿上长衫".PHP_EOL;
            $this->afterDraw();
        }
    }
    //警察装饰
    $police = new Person;
    $police->addDecorator(new PoliceDecorator);
    $police->clothes();
    //宇航员装饰
    $astronaut = new Person;
    $astronaut->addDecorator(new AstronautDecorator);
    $astronaut->clothes();
    //混搭风
    $madman = new Person;
    $madman->addDecorator(new PoliceDecorator);
    $madman->addDecorator(new AstronautDecorator);
    $madman->clothes();
    

    依赖注入模式

    依赖注入的实质就是把一个类不可能更换的部分 和 可更换的部分 分离开来,通过注入的方式来使用,从而达到解耦的目的

    数据库的配置是可以更换的部分

    class MysqlConfiguration
    {
        private $host;
        private $port;
        private $username;
        private $password;
        private $db_name;
        public function __construct(string $host, int $port, string $username, string $password,string $db_name)
        {
            $this->host = $host;
            $this->port = $port;
            $this->username = $username;
            $this->password = $password;
            $this->db_name = $db_name;
        }
        public function getHost(): string
        {
            return $this->host;
        }
        public function getPort(): int
        {
            return $this->port;
        }
        public function getUsername(): string
        {
            return $this->username;
        }
        public function getPassword(): string
        {
            return $this->password;
        }
        public function getDbName(): string
        {
            return $this->db_name;
        }
    }
    

    不可替换的部分

    class Mysql
    {
        private $configuration;
        public function __construct(MysqlConfiguration $config)
        {
            $this->configuration = $config;
        }
        public function connect(){
            return mysqli_connect($this->configuration->getHost(),$this->configuration->getUsername() ,$this->configuration->getPassword,$this->configuration->getDbName(),$this->configuration->getPort()); 
        }
    }
    

    配置文件和连接逻辑的分离
    使用

    $config = new MysqlConfiguration('127.0.0.1','root','','my_db',22);
    $db = new Mysql($config);
    $con = $db->connect();
    

    门面模式

    是指提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口。使用子系统更容易使用。(由一个门面(入口)把所有子系统隐藏起来了,只需要操作门面就可以)

    <?php
    /**
     * Created by PhpStorm.
     * User: LYL
     * Date: 2015/5/16
     * Time: 11:07
     */
     
    /**阿里股票
     * Class Ali
     */
    class Ali
    {
        function buy()
        {
            echo "买入阿里股票<br/>";
        }
     
        function sell()
        {
            echo "卖出阿里股票<br/>";
        }
    }
     
    /**万达股票
     * Class Wanda
     */
    class Wanda
    {
        function buy()
        {
            echo "买入万达股票<br/>";
        }
     
        function sell()
        {
            echo "卖出万达股票<br/>";
        }
    }
     
    /**京东股票
     * Class Jingdong
     */
    class Jingdong
    {
        function buy()
        {
            echo "买入京东股票<br/>";
        }
     
        function sell()
        {
            echo "卖出京东股票<br/>";
        }
    }
     
    /**门面模式核心角色
     * Class FacadeCompany
     */
    class FacadeCompany
    {
        private $ali;
     
        private $wanda;
     
        private $jingdong;
     
        function __construct()
        {
            $this->ali=new Ali();
            $this->jingdong=new Jingdong();
            $this->wanda=new Wanda();
        }
     
        function buy()
        {
            $this->wanda->buy();
            $this->ali->buy();
        }
     
        function sell()
        {
            $this->jingdong->sell();
        }
    }
    

    使用

    header("Content-Type:text/html;charset=utf-8");
    //------------------------门面模式测试代码------------------
    require_once "./Facade/Facade.php";
     
    $lurenA=new FacadeCompany();
    $lurenA->buy();
    $lurenA->sell();
    
    

    优势:
    1)它可以对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。
    2) 它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。松耦合关系使得子系统的组件变化不会影响到它的客户。 Facade模式有助于建立层次结构系统,也有助于对对象之间的依赖关系分层。
    3) Facade模式可以消除复杂的循环依赖关系。这一点在客户程序与子系统是分别实现的时候尤为重要。在大型软件系统中降低编译依赖性至关重要。在子系统类改变时,希望尽量减少重编译工作以节省时间。用Facade可以降低编译依赖性,限制重要系统中较小的变化所需的重编译工作。
    4)Facade模式同样也有利于简化系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。

    流接口模式(Fluent Interface)

    流式接口(fluent interface)是软件工程面向对象API的一种实现方式,以提供更为可读的源代码。

    通过被调方法的返回值定义
    自引用,新的上下文等于老的上下文。
    返回一个空的上下文来终止。

    代码

    <?php
    class Employee
    {
        public $name;
        public $surName; 
        public $salary;
    
        public function setName($name)
        {
            $this->name = $name;
    
            return $this;
        }
    
        public function setSurname($surname)
        {
            $this->surName = $surname;
    
            return $this;
        }
    
        public function setSalary($salary)
        {
            $this->salary = $salary;
    
            return $this;
        }
    
        public function __toString()
        {
            $employeeInfo = 'Name: ' . $this->name . PHP_EOL;
            $employeeInfo .= 'Surname: ' . $this->surName . PHP_EOL;
            $employeeInfo .= 'Salary: ' . $this->salary . PHP_EOL;
    
            return $employeeInfo;
        }
    }
    
    # Create a new instance of the Employee class, Tom Smith, with a salary of 100:
    $employee = (new Employee())
                    ->setName('Tom')
                    ->setSurname('Smith')
                    ->setSalary('100');
    
    # Display the value of the Employee instance:
    echo $employee;
    
    # Display:
    # Name: Tom
    # Surname: Smith
    # Salary: 100
    
    

    享元模式(Flyweight)

    使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元

    <?php
    //抽象享元对象
    abstract class Flyweight{
        //考点
        public $address;
        //享元角色必修设置考点
        public function __construct($address){
            $this->address = $address;
        }
    }
    //具体享元角色  考生类
    class ConcreteFlyweight extends Flyweight{
        //报考动作
        public function register(){
            echo "我的报考点是:{$this->address}".PHP_EOL;
        }
        //退出
        public function quit(){
            unset($this);
        }
    }
    //享元工厂 缓冲池
    class FlyweightFactor{
        static private $students = array();
        static public function getStudent($address){
            $students =self::$students;
            //判断该键值是否存在
            if(array_key_exists($address,$students)){
                echo "缓冲池有考点为{$address},从池中直接取".PHP_EOL;   
            }else{
                echo "缓冲池没有,创建了考点为{$address}的对象并放到池中".PHP_EOL;
                self::$students[$address] = new ConcreteFlyweight($address);
            }
            return self::$students[$address];
        }
    }
    
    //实例化学生对象
    $student_1 = FlyweightFactor::getStudent('广州');
    //报考
    $student_1 ->register();
    // 退出
    $student_1->quit();
    //第二个学生进来
    $student_2 = FlyweightFactor::getStudent('东莞');
    //报考
    $student_2 ->register();
    // 退出
    $student_2->quit();
    //第三个学生进来
    $student_3 = FlyweightFactor::getStudent('广州');
    //报考
    $student_3 ->register();
    // 退出
    $student_3->quit();
    

    代理模式

    构建了透明置于两个不同对象之内的一个对象,从而能够截取或代理这两个对象间的通信或访问{代理模式,就是在访问对象时通过一个代理对象去访问你想访问的对象。而在代理对象中,我们可以实现对访问对象的截断或权限控制等操作}

    <?php
    //代理抽象接口
    interface shop{
        public function buy($title);
    }
    //原来的CD商店,被代理对象
    class CDshop implements shop{
        public function buy($title){
            echo "购买成功,这是你的《{$title}》唱片".PHP_EOL;
        }
    }
    //CD代理
    class Proxy implements shop{
        public function buy($title){
            $this->go();
            $CDshop = new CDshop;
            $CDshop->buy($title);
        }
        public function go(){
            echo "跑去香港代购".PHP_EOL;
        }
    }
    
    //你93年买了张 吻别
    $CDshop = new CDshop;
    $CDshop->buy("吻别");
    //14年你想买张 醒着做梦 找不到CD商店了,和做梦似的,不得不找了个代理去香港帮你代购。
    $proxy = new Proxy;
    $proxy->buy("醒着做梦");
    

    注册树模式(Registry Pattern)

    把多个类的实例注册到一个注册器类中去,然后需要哪个类,由这个注册器类统一调取

    <?php
    //User类用于测试
    class User{}
    
    //注册树类
    class Registry
    {
        protected static $objects;  //用于存放实例
        //存入实例方法
        static public function set($key, $object)
        {
            self::$objects[$key] = $object;
        }
        //获取实例方法
        static public function get($key)
        {
            if (!isset(self::$objects[$key]))
            {
                return false;
            }
            return self::$objects[$key];
        }
        //删除实例方法
        static public function _unset($key)
        {
            unset(self::$objects[$key]);
        }
    }
    
    
    $user = new User;
    //存入实例
    Registry::set('User',$user);
    //查看实例
    var_dump(Registry::get('User'));
    //删除实例
    Registry::_unset('User');
    //再次查看实例
    var_dump(Registry::get('User'));
    

    行为型

    责任链模式(Chain Of Responsibilities)

    责任链是一种比较高级的行为设计模式,就是当你有一个请求,你不知道用那个方法(handler)来处理这个请求时,你可以把这个请求丢进一个责任链里(里面有很多方法),这个责任链会通过轮询的方式自动找到对应的方法

    <?php
    //抽象处理者
    abstract class Handler{
        private $next_handler ;//存放下一处理对象
        private $lever = 0;             //处理级别默认0
        abstract protected function response();  //处理者回应
        //设置处理级别
        public function setHandlerLevel($lever){
            $this->lever = $lever ;
        }
        //设置下一个处理者是谁
        public function setNext(Handler $handler){
            $this->next_handler = $handler;
            $this->next_handler->setHandlerLevel($this->lever+1);
        }
        //责任链实现主要方法,判断责任是否属于该对象,不属于则转发给下一级。使用final不允许重写
        final public function handlerMessage(Request $request){
            if($this->lever == $request->getLever()){
                $this->response();
            }else{
                if($this->next_handler != null){
                    $this->next_handler->handlerMessage($request);
                }else{
                    echo '洗洗睡吧,没人理你'.PHP_EOL;
                }
            }
        }
    }
    //具体处理者
    // headman 组长 
    class HeadMan extends Handler{
        protected function response(){
            echo '组长回复你:同意你的请求'.PHP_EOL;
        }
    }
    //主管director
    class Director extends Handler{
        protected function response(){
            echo '主管回复你:知道了,退下'.PHP_EOL;
        }
    }
    //经理manager
    class Manager extends Handler{
        protected function response(){
            echo '经理回复你:容朕思虑,再议'.PHP_EOL;
        }
    }
    //请求类
    class Request{
        protected $level = array('请假'=>0,'休假'=>1,'辞职'=>2);//测试方便,默认设置好请示级别对应关系
        protected $request;
        public function __construct($request){
            $this->request = $request;
        }
        public function getLever(){
            //获取请求对应的级别,如果该请求没有对应级别 则返回-1
            return array_key_exists($this->request,$this->level)?$this->level[$this->request]:-1;
        }
    }
    
    //实例化处理者
    $headman = new HeadMan;
    $director = new Director;
    $manager = new Manager;
    //责任链组装
    $headman->setNext($director);
    $director->setNext($manager);
    //传递请求
    $headman->handlerMessage(new Request('请假'));
    $headman->handlerMessage(new Request('休假'));
    $headman->handlerMessage(new Request('辞职'));
    $headman->handlerMessage(new Request('加薪'));
    

    命令模式(Command Pattern)

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

    <?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();
    

    在实际使用中,命令模式的receiver经常是一个抽象类,就是对于不同的命令,它都有对应的具体命令接收者。命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。

    迭代模式(Iterator Pattern)

    迭代器模式:迭代器模式是遍历集合的成熟模式,迭代器模式的关键是将遍历集合的任务交给一个叫做迭代器的对象,它的工作时遍历并选择序列中的对象,而客户端程序员不必知道或关心该集合序列底层的结构。
    Iterator(迭代器):迭代器定义访问和遍历元素的接口
    ConcreteIterator(具体迭代器):具体迭代器实现迭代器接口,对该聚合遍历时跟踪当前位置
    Aggregate (聚合):聚合定义创建相应迭代器对象的接口(可选)
    ConcreteAggregate(具体聚合):具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例(可选)

    <?php
    class ArrayContainer implements Iterator
    {
        protected $data = array();
        protected $index ;
    
        public function __construct($data)
        {   
            $this->data = $data;
        }
        //返回当前指针指向数据
        public function current()
        {   
            return $this->data[$this->index];
        }
        //指针+1
        public function next()
        {   
            $this->index ++;
        }
        //验证指针是否越界
        public function valid()
        {
            return $this->index < count($this->data);
        }
        //重置指针
        public function rewind()
        {
            $this->index = 0;
        }
        //返回当前指针
        public function key()
        {   
            return $this->index;
        }
    }
    
    //初始化数组容器
    $arr = array(0=>'唐朝',1=>'宋朝',2=>'元朝');
    $container = new ArrayContainer($arr);
    
    //遍历数组容器
    foreach($container as $a => $dynasty){
        echo '如果有时光机,我想去'.$dynasty.PHP_EOL;
    }
    

    中介者模式(Mediator Pattern)

    中介者模式用一个中介者对象来封装一系列的对象交互。中介者使得各对象不需要显式地相互引用,从而使其松散耦合,而且可以独立地改变它们之间的交互
    角色:
    中介者接口(UnitedNations):在里面定义了各个同事之间相互交互所需要的方法。
    具体的中介者实现对象(UnitedCommit):它需要了解并为维护每个同事对象,并负责具体的协调各个同事对象的交互关系。
    同事类的定义(Country):通常实现成为抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能,
    具体的同事类(China):实现自己的业务,需要与其他同事对象交互时,就通知中介对象,中介对象会负责后续的交互。

    <?php
    //抽象同事类 --------电话机
    abstract class Colleague{
        protected $mediator;    //用于存放中介者
        abstract public function sendMsg($num,$msg);
        abstract public function receiveMsg($msg);
        //设置中介者
        final public function setMediator(Mediator $mediator){
          $this->mediator = $mediator;
        }
    }
    //具体同事类 ---------座机
    class Phone extends Colleague
    {
        public function sendMsg($num,$msg)
        {
          echo __class__.'--发送声音:'.$msg.PHP_EOL;
          $this->mediator->opreation($num,$msg);
        }
    
        public function receiveMsg($msg)
        {
          echo __class__.'--接收声音:'.$msg.PHP_EOL;
        }
    }
    //具体同事类----------手机
    class Telephone extends Colleague
    {
        public function sendMsg($num,$msg)
        {
            echo __class__.'--发送声音:'.$msg.PHP_EOL;
            $this->mediator->opreation($num,$msg);
        }
        //手机接收信息前 会智能响铃
        public function receiveMsg($msg)
        {   
            echo '响铃-------'.PHP_EOL;
            echo __class__.'--接收声音:'.$msg.PHP_EOL;
        }
    }
    //抽象中介者类
    abstract class Mediator{
      abstract public function opreation($id,$message);
      abstract public function register($id,Colleague $colleague);
    }
    //具体中介者类------交换机
    class switches extends Mediator
    {
        protected  $colleagues = array();
        //交换机业务处理
        public function opreation($num,$message)
        {
            if (!array_key_exists($num,$this->colleagues)) {
                echo __class__.'--交换机内没有此号码信息,无法通话'.PHP_EOL;
            }else{
                $this->colleagues[$num]->receiveMsg($message);
            }
        }
        //注册号码
        public function register($num,Colleague $colleague)
        {
          if (!in_array($colleague, $this->colleagues)) {
              $this->colleagues[$num] = $colleague;
          }
          $colleague->setMediator($this);
        }
    }
    //实例化固话
    $phone = new Phone;
    //实例化手机
    $telephone = new Telephone;
    //实例化交换机
    $switches = new Switches;
    //注册号码  ---放号
    $switches->register(6686668,$phone);
    $switches->register(18813290000,$telephone);
    //通话
    $phone->sendMsg(18813290000,'hello world');
    $telephone->sendMsg(6686668,'请说普通话');
    $telephone->sendMsg(6686660,'你好');
    

    备忘录模式

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

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

    <?php
    //发起人,所需备份者
    class Originator{
        //内部状态
        private $state;
        //设置状态
        public function setState($state){
            $this->state = $state ;
        }
        //查看状态
        public function getState(){
            echo $this->state,PHP_EOL;
        }
        //创建一个备份录
        public function createMemento(){
            return new Memento($this->state);
        }
        //恢复一个备份
        public function restoreMemento(Memento $memento){
            $this->state = $memento->getState();
        }
    }
    //备忘录角色
    class Memento{
        private $state; //用于存放发起人备份时的状态
        public function __construct($state){
            $this->state = $state;
        }
        public function getState(){
            return $this->state;
        }
    }
    //备忘录管理者
    class Caretaker{
        private $menento;
        //存档备忘录
        public function setMemento(Memento $memento){
            $this->memento = $memento;
        }
        //取出备忘录
        public function getMemento(){
            return $this->memento;
        }
    }
    
    //实例化发起人 假如是个游戏角色
    $role = new Originator;
    //设置状态 满血
    $role->setState('满血');
    //备份
    //创建备份录管理者
    $caretaker = new Caretaker;
    //创建备份
    $caretaker->setMemento($role->createMemento());
    //状态更改
    $role->setState('死亡');
    $role->getState();
    //恢复备份
    $role->restoreMemento($caretaker->getMemento());
    //重新查看状态
    $role->getState();
    

    空对象模式(Null Object Pattern)
    空对象模式(Null Object Pattern):用一个空对象取代 NULL,减少对实例的检查。这样的空对象可以在数据不可用的时候提供默认的行为

    interface Animal {
        public function makeSound();
    }
    class Dog implements Animal {
        public function makeSound() { 
            echo "Woof.."; 
        }
    }
    class Cat implements Animal {
        public function makeSound() { 
            echo "Meowww.."; 
        }
    }
    //这个就是空对象,里面的方法啥也不做,它的存在就是为了避免报错
    class NullAnimal implements Animal {
        public function makeSound() { 
            // silence...
        }
    }
    $animalType = 'elephant';
    switch($animalType) {
        case 'dog':
            $animal = new Dog();
            break;
        case 'cat':
            $animal = new Cat();
            break;
        default:
            $animal = new NullAnimal();
            break;
    }
    $animal->makeSound(); // ..the null animal makes no sound
    

    观察者模式

    观察者模式对应了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题发生变化时,会通知所有观察者对象,使他们能够自己自动更新

    重要角色:
    抽象通知者角色(INotifier):定义了通知的接口规则。
    具体通知者角色(Boss):实现抽象通知者的接口,接到状态改变立即向观察者下发通知。
    抽象观察者角色(IObserver):定义接到通知后所做的操作(Update)接口规则。
    具体观察者角色(JingDong):实现具体操作方法。

    <?php
    //抽象被观察者
    abstract class Subject{
        //定义一个观察者数组
        private $observers = array();
        //增加观察者方法
        public function addObserver(Observer $observer){
            $this->observers[] = $observer;
            echo "添加观察者成功".PHP_EOL;
        }
        //删除观察者方法
        public function delObserver(Observer $observer){
            $key = array_search($observer,$this->observers); //判断是否有该观察者存在
            if($observer===$this->observers[$key]) { //值虽然相同 但有可能不是同一个对象 ,所以使用全等判断
                unset($this->observers[$key]);
                echo '删除观察者成功'.PHP_EOL;
            } else{
                echo '观察者不存在,无需删除'.PHP_EOL;
            }
        }
        //通知所有观察者
        public function notifyObservers(){
            foreach($this->observers as $observer){
                $observer->update();
            }
        }
    }
    //具体被观察者 服务端
    class Server extends Subject{
        //具体被观察者业务 发布一条信息,并通知所有客户端
        public function publish(){
            echo '今天天气很好,我发布了更新包'.PHP_EOL;
            $this->notifyObservers();
        }
    }
    //抽象观察者接口
    Interface Observer{
        public function update();
    }
    //具体观察者类
    //微信端
    class Wechat implements Observer{
        public function update(){
            echo '通知已接收,微信更新完毕'.PHP_EOL;
        }
    }
    //web端
    class Web implements Observer{
        public function update(){
            echo '通知已接收,web端系统更新中'.PHP_EOL;
        }
    }
    //app端
    class App implements Observer{
        public function update(){
            echo '通知已接收,APP端稍后更新'.PHP_EOL;
        }
    }
    
    //实例化被观察者
    $server = new Server ;
    //实例化观察者
    $wechat = new Wechat ;
    $web = new Web ;
    $app = new App;
    //添加被观察者
    $server->addObserver($wechat);
    $server->addObserver($web);
    $server->addObserver($app);
    //被观察者 发布信息
    $server->publish();
    
    //删除观察者
    $server->delObserver($wechat);
    //再次发布信息
    $server->publish();
    
    //尝试删除一个未添加成观察者的对象
    $server->delObserver(new Web);
    //再次发布信息
    $server->publish();
    

    规格模式

    规格模式是组合模式的一种扩展,在框架性开发中使用较多(项目级开发很少使用),这里做一个简单的介绍。
    规格模式(Specification)可以认为是组合模式的一种扩展。有时项目中某些条件决定了业务逻辑,这些条件就可以抽离出来以某种关系(与、或、非)进行组合,从而灵活地对业务逻辑进行定制。另外,在查询、过滤等应用场合中,通过预定义多个条件,然后使用这些条件的组合来处理查询或过滤,而不是使用逻辑判断语句来处理,可以简化整个实现逻辑。

    这里的每个条件就是一个规格,多个规格/条件通过串联的方式以某种逻辑关系形成一个组合式的规格。

    策略模式 ( Strategy Pattern )

    策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)
    1,在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
    2,利用面向对象的继承和多态机制,将多个算法解耦。避免类中出现太多的if-else语句

    角色分析:
    抽象策略角色(RotateItem):策略类,通常由一个接口或者抽象类实现。
    具体策略角色(ItemX):包装了相关的算法和行为。
    环境角色(ItemContext):持有一个策略类的引用,最终给客户端调用。

    <?php
    //抽象策略接口
    abstract class Strategy{
      abstract function peddle();
    }
    
    //具体封装的策略方法   女性用户策略
    class ConcreteStrategyA extends Strategy
    {
        public function peddle(){
            echo '美女穿这件衣服肯定很好看'.PHP_EOL;
        }
    }
    
    //男性用户策略
    class ConcreteStrategyB extends Strategy
    {
        public function peddle(){
            echo '每一个男人都需要一个工具箱,充实工具箱,从购买各种螺丝刀开始'.PHP_EOL;
        }
    
    }
    
    //未知性别用户策略
    class ConcreteStrategyC extends Strategy
    {
      public function peddle(){
            echo '骨骼清奇,这本《葵花宝典》最适合你'.PHP_EOL;
        }
    }
    
    //环境类
    class Context{
        protected $strategy;
    
        public function __construct(Strategy $strategy)
        {
          $this->strategy = $strategy;
        }
    
        public function request()
        {
          $this->strategy->peddle($this);
        }
    }
    
    //若处于女性用户环境
    $female_context = new Context(new ConcreteStrategyA);
    $female_context->request();
    //若处于男性用户环境
    $male_context = new Context(new ConcreteStrategyB);
    $male_context->request();
    //若处于未知性别用户环境
    $unknow_context = new Context(new ConcreteStrategyC);
    $unknow_context->request();
    

    状态模式 (State Pattern)

    允许一个对象在其内部状态改变时改变它的行为,让不同状态的对象看起来似乎修改了它的类,或者说是看起来不是来自同一个类。

    好处:
    1,将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
    2,本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所要的这些状态的版本。

    角色
    上下文环境(Work):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的具体对象来处理。
    抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
    具体状态(AmState):实现抽象状态定义的接口。

    <?php
    //抽象状态类
    abstract class State{
      abstract function handle();
    }
    //固态
    class Solid extends State{
        public function handle(){
            echo '固态 =>融化 =>液态转化中'.PHP_EOL;
        }
    }
    class Liquid extends State{
        public function handle(){
            echo '液态 =>蒸发 =>气态转化中'.PHP_EOL;
        }
    }
    class Gas extends State{
        public function handle(){
            echo '气态 =>凝华 =>固态转化中'.PHP_EOL;
        }
    }
    //context环境类 -----water
    class Water{
      protected $states = array();
      protected $current=0;
      public function __construct()
      {
          $this->states[]=new Solid;
          $this->states[]=new Liquid;
          $this->states[]=new Gas;
      }
      //水的变化
      public function change(){
        //告知当前状态
        echo '当前所处状态'.get_Class($this->states[$this->current]).PHP_EOL;
        //当前状态能力
        $this->states[$this->current]->handle();
        //状态变化
        $this->changeState();
      }
      //状态变化
      public function changeState()
      {
          $this->current++ == 2 && $this->current = 0;
      }
    }
    
    
    
    //实例化具体环境角色-----水
    $water = new Water;
    //水的能力变化   ---与它的状态相关
    $water->change();
    $water->change();
    $water->change();
    $water->change();
    

    模板方法模式(Template Method)

    父类提供一系列模板方法,有的实现了逻辑,有的只是一个接口。而子类继承大部分共有方法,同时对接口方法进行不同的实现,从而完成对父类模板的个性化改造,起到一对多的解耦目的。
    可以说PHP的抽象类就是为了实现这个设计模式而推出的功能,在PHP中,抽象类本身就是模板方法模式

    角色:
    抽象模板角色(MakePhone):抽象模板类,定义了一个具体的算法流程和一些留给子类必须实现的抽象方法。
    具体子类角色(XiaoMi):实现MakePhone中的抽象方法,子类可以有自己独特的实现形式,但是执行流程受MakePhone控制。

    <?php
    /**
     * Created by PhpStorm->
     * User  extends  Jang
     * Date  extends  2015/6/10
     * Time  extends  11  extends 06
     */
     
    //抽象模板类
    abstract class MakePhone
    {
        protected $name;
     
        public function __construct($name)
        {
            $this->name=$name;
        }
     
        public function MakeFlow()
        {
            $this->MakeBattery();
            $this->MakeCamera();
            $this->MakeScreen();
            echo $this->name."手机生产完毕!<hr/>";
        }
        public abstract function MakeScreen();
        public abstract function MakeBattery();
        public abstract function MakeCamera();
    }
     
    //小米手机
    class XiaoMi extends MakePhone
    {
        public function __construct($name='小米')
        {
            parent::__construct($name);
        }
     
        public   function MakeBattery()
        {
            echo "小米电池生产完毕!<br/>";
        }
        public   function MakeCamera()
        {
            echo "小米相机生产完毕!<br/>";
        }
     
        public  function MakeScreen()
        {
            echo "小米屏幕生产完毕!<br/>";
        }
    }
     
    //魅族手机
    class FlyMe  extends  MakePhone
    {
        function __construct($name='魅族')
        {
            parent::__construct($name);
        }
     
        public   function MakeBattery()
        {
            echo "魅族电池生产完毕!<br/>";
        }
        public   function MakeCamera()
        {
            echo "魅族相机生产完毕!<br/>";
        }
     
        public   function MakeScreen()
        {
            echo "魅族屏幕生产完毕!<br/>";
        }
    }
    
    
    调用
    header("Content-Type:text/html;charset=utf-8");
    //-------------------------模板模式---------------------
    require_once "./Template/Template.php";
    $miui=new XiaoMi();
    $flyMe=new FlyMe();
     
    $miui->MakeFlow();
    $flyMe->MakeFlow();
    

    访问者模式(Visitor Pattern)

    访问者模式(Visitor Pattern) : 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。(访问者模式能够在不更改对象的情况下向该对象添加新的功能性)

    角色:
    1.抽象访问者(State):为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它。
    2.具体访问者(Success):实现访问者声明的接口。
    3.抽象元素(Person):定义一个接受访问操作accept(),它以一个访问者作为参数。
    4. 具体元素(Man):实现了抽象元素所定义的接受操作接口。
    5.结构对象(ObjectStruct):这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)。

    <?php
    //具体元素
    class Superman{
        public $name;
        public function doSomething(){
            echo '我是超人,我会飞'.PHP_EOL;
        }
        public function accept(Visitor $visitor){
            $visitor->doSomething($this);
        }
    }
    //具体访问者
    class Visitor{
        public function doSomething($object){
            echo '我可以返老还童到'.$object->age = 18;
        }
    }
    //实例化具体对象
    $man = new Superman;
    //使用自己的能力
    $man->doSomething();
    //通过添加访问者,把访问者能能力扩展成自己的
    $man->accept(new Visitor);
    

    更多类型

    委托模式(Delegation)

    一个对象将它要执行的任务委派给与之关联的帮助对象去执行。在示例中,“组长”声明了writeCode方法并使用它,其实「组长」把writeCode委托给“菜鸟开发者”的writeBadCode方法做了。这种反转责任的做法隐藏了其内部执行writeBadCode的细节。

    设计了一个cd类,类中有mp3播放模式,和mp4播放模式
    改进前,使用cd类的播放模式,需要在实例化的类中去判断选择什么方式的播放模式
    改进后,播放模式当做一个参数传入playList函数中,就自动能找到对应需要播放的方法。

    未改进前

    <?php  
    //使用委托模式之前,调用cd类,选择cd播放模式是复杂的选择过程  
    class cd {  
        protected $cdInfo = array();   
          
        public function addSong($song) {  
            $this->cdInfo[$song] = $song;  
        }  
          
        public function playMp3($song) {  
            return $this->cdInfo[$song] . '.mp3';  
        }  
          
        public function playMp4($song) {  
            return $this->cdInfo[$song] . '.mp4';  
        }  
    }  
    $oldCd = new cd;  
    $oldCd->addSong("1");  
    $oldCd->addSong("2");  
    $oldCd->addSong("3");  
    $type = 'mp3';  
    if ($type == 'mp3') {  
        $oldCd->playMp3();  
    } else {  
        $oldCd->playMp4();  
    }
    

    二、通过委托模式,改进后的cd类

    <?php
    
    namespace Tools;
    
    /*
    委托模式
    去除核心对象中的判决和复杂功能性
    */
    
    //委托接口
    interface Delegate{
        public function playList($list,$song);
    }
    
    //mp3处理类
    class mp3 implements Delegate{
        public function playList($list,$song){
            return $list[$song].'.mp3';
        }
    }
    
    //mp4处理类
    class mp4 implements Delegate{
        public function playList($list, $song)
        {
            return $list[$song].'.mp4';
        }
    }
    
    class cdDelegate{
        protected $cdInfo = array();
    
        public function addSong($song){
            $this->cdInfo[$song] = $song;
        }
    
        public function play($type,$song){
            $name = '\Tools\\'.$type;
            $obj =  new $name;
            return $obj->playList($this->cdInfo,$song);
        }
    }
    
    $newCd = new cdDelegate();
    $newCd->addSong("1");
    $newCd->addSong("2");
    $newCd->addSong("3");
    echo $newCd->play('mp3','1');//只要传递参数就能知道需要选择何种播放模式
    

    服务定位器模式(Service Locator)

    服务定位器(service locator)他知道如何定位(创建或者获取)一个应用所需要的服务,服务使用者在实际使用中无需关心服务的实际实现。{实现服务使用者和服务的解耦,无需改变代码而只是通过简单配置更服服务实现。}

    class ServiceLocator {
    
        /**
         * 服务实例索引
         */
        privite $_services = [];
    
        /**
         * 服务定义索引
         */
        private $_definitions = [];
        
        /**
         * 是否全局服务共享(单例模式)
         */
        private $_shared = [];
        
        public function has($id){
            return isset($this->_services[$id]) || isset($this->_definitions[$id]);
        }
        
        public function __get($id){
            if($this->has($this->id)){
                $this->get($id);
            }
            
            // another implement
        }
        
        public function get($id){
            if(isset($this->_services[$id]) && $this->_shared[$id]){
                return $this->_services[$id];
            }
            
            if (isset($this->_definitions[$id])) {
                // 实例化
                $definition = $this->_definitions[$id];
                $object = Creator::createObject($definition);//省略服务实例化实现
                if($this->_shared[$id]){
                    $this->_services[$id] = $object
                }
                
                return $object;
            }
            
            throw new Exception("无法定位服务{$id}")
        }
            
        public function set($id,$definition,$share = false){
            if ($definition === null) {
                unset($this->_services[$id], $this->_definitions[$id]);
                return;
            }
            
            unset($this->_services[$id]);
            $this->_shared[$id] = $share;
            if (is_string($definition)) {
                return $this->_definitions[$id] = $definition;
            }
            if (is_object($definition) || is_callable($definition, true)) {
                return $this->_definitions[$id] = $definition;
            }
            
            if (is_array($definition)) {
                if (isset($definition['class'])) {
                    return $this->_definitions[$id] = $definition;
                }
            }
            
            throw new Exception("服务添加失败");
        }
    }
    

    资源库模式

    才看到这个设计模式的时候并不觉得有多大用处,并没有发现会使代码简介多少。但是用过之后不得不说它的优势是很明显的,在M层和C层交互的时候,很好的解决了代码的耦合问题。
    模式定义:Repository 是一个独立的层,介于领域层与数据映射层(数据访问层)之间。它的存在让领域层感觉不到数据访问层的存在,它提供一个类似集合的接口提供给领域层进行领域对象的访问。Repository 是仓库管理员,领域层需要什么东西只需告诉仓库管理员,由仓库管理员把东西拿给它,并不需要知道东西实际放在哪。
    Repository 模式是架构模式,在设计架构时,才有参考价值。应用 Repository 模式所带来的好处,远高于实现这个模式所增加的代码。只要项目分层,都应当使用这个模式。
    实现方法:
    1:首先使用 Trait 创建一个基类,里面封装好对数据表常见的增删改查操作,方便后面的资源文件分别调用。
    2.定义mvc中的m层,同时定义一个仓库文件用于操作这个模型,在改仓库文件上 use 上面定义的基类别,将其引入进来。
    3.在仓库文件下定义一个受保护的 model 因为基类采用的是this->model 来代表当前的模型,然后在仓库文件的构造方法里,把要操作的模型类实例化出来赋值给$model ,这样我们就可以在仓库文件中对数据表进行操作了,当基类方法不够用使,我们可以在相应的仓库文件中添加模型方法。
    4.只要项目中用到mvc分层,都可以采取这种设计模式,我觉得很好的优化了代码,逻辑更清晰,需要什么输入的时候控制器基本不用动,只需修改相应的仓库文件即可。

    Eva模式

    实体属性值(Entity - attribute - value EAV)模式,可以方便PHP实现EAV模型。

    实体属性值模型(Entity-attribute-value EAV)是一种用数据模型描述实体的属性(属性,参数),可以用来形容他们潜在巨大,但实际上将适用于给定的实体的数量是相对更少。在数学中,这种模式被称为一个稀疏矩阵.EAV也被称为对象的属性值的模式,垂直的数据库模型和开放式架构。

    反面模式

    在软件工程中,一个反面模式(Anti-pattern 或 Antipattern)指的是在实践中明显出现但又低效或是有待优化的设计模式,是用来解决问题的带有共同性的不良方法。它们已经经过研究并分类,以防止日后重蹈覆辙,并能在研发尚未投产的系统时辨认出来。

    反面模式具体见:
    http://www.labazhou.net/2015/05/nine-anti-patterns-every-programmer-should-be-aware-of-with-examples/

    相关文章

      网友评论

        本文标题:php设计模式

        本文链接:https://www.haomeiwen.com/subject/objtmftx.html