记录PHP常用几种设计模式

Posted 雲夜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记录PHP常用几种设计模式相关的知识,希望对你有一定的参考价值。

什么是设计模式

每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动。

主要设计模式

创建型

  • 单例(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点
  • 抽象工厂(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口
  • 工厂方法(Factory Method):定义一个用于创建对象的接口,让子类决定哪一个类实例化
  • 原型(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象

结构型

  • 适配器(Adapter):将一个类的接口转换成期望的另一个接口
  • 代理(Proxy):为其他对象提供一个代理以控制对这个对象的访问

行为型

  • 备忘录(Memento):备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态
  • 观察者(Observer):在对象间定义一个一对多的联系性,由此当一个对象改变了状态,所有其他相关的对象会被通知并且自动刷新
  • 策略(Strategy):定义一个算法的系列,将其各个分装,并且使他们有交互性。策略模式使得算法在用户使用的时候能独立的改变

单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点。让类自身负责保存它的唯一实例,并提供一个访问该实例的方法。这就是单例模式

适用性

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时

优点

  • 对唯一实例的受控访问
  • 避免全局变量污染
  • 允许对操作和表示精化
  • 允许可变数目的实例
  • 比类操作更灵活

例子:

<?php
  
/**
* Singleton of Database
*/
class Database
{
  // We need a static private variable to store a Database instance.
  privatestatic $instance;
  
  // Mark as private to prevent it from being instanced.
  private function__construct()
  {
    // Do nothing.
  }
  
  private function__clone() 
  {
    // Do nothing.
  }
  
  public static function getInstance() 
  {
    if (!(self::$instance instanceof self)) {
      self::$instance = new self();
    }
  
    return self::$instance;
  }
}
  
$a =Database::getInstance();
$b =Database::getInstance();
  
// true
var_dump($a === $b);

抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类

“工厂”是创建产品(对象)的地方,其目的是将产品的创建与产品的使用分离。抽象工厂模式的目的,是将若干抽象产品的接口与不同主题产品的具体实现分离开。这样就能在增加新的具体工厂的时候,不用修改引用抽象工厂的客户端代码

适用性

  • 一个系统要独立于它的产品的创建、组合和表示时
  • 一个系统要由多个产品系列中的一个来配置时
  • 需要强调一系列相关的产品对象的设计以便进行联合使用时
  • 提供一个产品类库,而只想显示它们的接口而不是实现时

优点

  • 具体产品从客户代码中被分离出来
  • 容易改变产品的系列
  • 将一个系列的产品族统一到一起创建

缺点

  • 在产品族中扩展新的产品是很困难的,它需要修改抽象工厂的接口

工厂方法模式

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类

适用性

  • 当一个类不知道它所必须创建的对象的类的时候
  • 当一个类希望由它的子类来指定它所创建的对象的时候
  • 当类将创建对象的职责委托给多个帮忙子类的中的某一个,并且你希望将哪一个帮助子类是代理者者一信息局部化的时候

例子:

<?php
      
    interface InterfaceShape
    {
     function getArea();
     function getCircumference();
    }
      
    /**
    * 矩形
    */
    class Rectangle implements InterfaceShape
    {
      private $width;
      private $height;
       
      public function __construct($width, $height)
      {
        $this->width = $width;
        $this->height = $height;
      }
      
      public function getArea()
      {
        return $this->width* $this->height;
      }
      
      public function getCircumference()
      {
        return 2 * $this->width + 2 * $this->height;
      }
    }
      
    /**
    * 圆形
    */
    class Circle implements InterfaceShape
    {
      private $radius;
      
      function __construct($radius)
      {
        $this->radius = $radius;
      }
      
      
      public function getArea()
      {
        return M_PI * pow($this->radius, 2);
      }
      
      public function getCircumference()
      {
        return 2 * M_PI * $this->radius;
      }
    }
      
    /**
    * 形状工厂类
    */
    class FactoryShape
    {
      public static function create()
      {
        switch (func_num_args()) {
          case1:
          return newCircle(func_get_arg(0));
          case2:
          return newRectangle(func_get_arg(0), func_get_arg(1));
          default:
            # code...
            break;
        }
      }
    }
      
    $rect =FactoryShape::create(5, 5);
    // object(Rectangle)#1 (2) { ["width":"Rectangle":private]=> int(5) ["height":"Rectangle":private]=> int(5) }
    var_dump($rect);
    echo "<br>";
      
    // object(Circle)#2 (1) { ["radius":"Circle":private]=> int(4) }
    $circle =FactoryShape::create(4);
    var_dump($circle);

适配器模式

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

适用性

  • 你想使用一个已经存在的类,而它的接口不符合你的需求
  • 你想创建一个可以复用的类,改类可以与其他不相关的类或不可预见(可能不兼容)的类协同工作
  • 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口

例子:

//老的代码     
     
    class User {    
     
        private $name;    
     
        function __construct($name) {    
     
            $this->name = $name;    
     
        }    
     
        public function getName() {    
     
            return $this->name;    
     
        }    
     
    }   
    //新代码,开放平台标准接口    
     
    interface UserInterface {    
     
        function getUserName();    
     
    }    
     
    class UserInfo implements UserInterface {    
     
        protected $user;    
     
        function __construct($user) {    
     
            $this->user = $user;    
     
        }    
     
        public function getUserName() {    
     
            return $this->user->getName();    
     
        }    
     
    }   
    $olduser = new User(\'张三\');    
     
    echo $olduser->getName()."n";    
     
    $newuser = new UserInfo($olduser);    
     
    echo $newuser->getUserName()."n";

 

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

适用性

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用
  • 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变
  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不知道这些对象时紧密耦合的

例子:

<?php
      
    /*
    观察者接口
    */
    interface InterfaceObserver
    {
      function onListen($sender, $args);
      function getObserverName();
    }
      
    // 可被观察者接口
    interface InterfaceObservable
    {
      function addObserver($observer);
      function removeObserver($observer_name);
    }
      
    // 观察者抽象类
    abstract class Observer implements InterfaceObserver
    {
      protected $observer_name;
      
      function getObserverName()
      {
        return $this->observer_name;
      }
      
      function onListen($sender, $args)
      {
      
      }
    }
      
    // 可被观察类
    abstract class Observable implements InterfaceObservable
    {
      protected $observers = array();
      
      public function addObserver($observer)
      {
        if ($observerinstanceofInterfaceObserver)
        {
          $this->observers[] = $observer;
        }
      }
      
      public function removeObserver($observer_name)
      {
        foreach ($this->observersas $index => $observer)
        {
          if ($observer->getObserverName() === $observer_name)
          {
            array_splice($this->observers, $index, 1);
            return;
          }
        }
      }
    }
      
    // 模拟一个可以被观察的类
    class A extends Observable
    {
      public function addListener($listener)
      {
        foreach ($this->observersas $observer)
        {
          $observer->onListen($this, $listener);
        }
      }
    }
      
    // 模拟一个观察者类
    class B extends Observer
    {
      protected $observer_name = \'B\';
      
      public function onListen($sender, $args)
      {
        var_dump($sender);
        echo "<br>";
        var_dump($args);
        echo "<br>";
      }
    }
      
    // 模拟另外一个观察者类
    class C extends Observer
    {
      protected $observer_name = \'C\';
      
      public function onListen($sender, $args)
      {
        var_dump($sender);
        echo "<br>";
        var_dump($args);
        echo "<br>";
      }
    }
      
    $a = new A();
    // 注入观察者
    $a->addObserver(new B());
    $a->addObserver(new C());
      
    // 可以看到观察到的信息
    $a->addListener(\'D\');
      
    // 移除观察者
    $a->removeObserver(\'B\');
      
    // 打印的信息:
    // object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }
    // string(1) "D"
    // object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }
    // string(1) "D"

 

策略模式

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化

适用性

  • 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法
  • 需要使用一个算法的不同变体
  • 算法使用客户不应该知道的数据。避免暴露复杂的、与算法相关的数据结构
  • 一个类定义了多种行为,并且这些行为在类的操作中以多个条件语句的形式出现

例子:

MaleUserStrategy.php

<?php
     
    namespace IMooc;
     
     
    interface UserStrategy {
        function showAd();
        function showCategory();
    }

MaleUserStrategy.php

<?php
     
    namespace IMooc;
     
     
    class MaleUserStrategy implements UserStrategy  {
     
        function showAd()
        {
            echo "IPhone6";
        }
     
        function showCategory()
        {
            echo "电子产品";
        }
    }

FemaleUserStrategy.php

<?php
     
    namespace IMooc;
     
    class FemaleUserStrategy implements UserStrategy {
        function showAd()
        {
            echo "2014新款女装";
        }
        function showCategory()
        {
            echo "女装";
        }
    }
<?php
    interface FlyBehavior{
        public function fly();
    }
     
    class FlyWithWings implements FlyBehavior{
        public function fly(){
            echo "Fly With Wings \\n";
        }
    }
     
    class FlyWithNo implements FlyBehavior{
        public function fly(){
            echo "Fly With No Wings \\n";
        }
    }
    class Duck{
        private $_flyBehavior;
        public function performFly(){
            $this->_flyBehavior->fly();
        }
     
        public function setFlyBehavior(FlyBehavior $behavior){
            $this->_flyBehavior = $behavior;
        }
    }
     
    class RubberDuck extends Duck{
    }
    // Test Case
    $duck = new RubberDuck();
     
    /*  想让鸭子用翅膀飞行 */
    $duck->setFlyBehavior(new FlyWithWings());
    $duck->performFly();            
     
    /*  想让鸭子不用翅膀飞行 */
    $duck->setFlyBehavior(new FlyWithNo());
    $duck->performFly();

参考:

https://www.cnblogs.com/lxwphp/p/10484661.html

https://github.com/colinlet/PHP-Interview-QA/blob/master/docs/09.%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/QA.md

以上是关于记录PHP常用几种设计模式的主要内容,如果未能解决你的问题,请参考以下文章

PHP常用代码片段

记录C#常用的代码片段

21个常用代码片段

提效小技巧——记录那些不常用的代码片段

56个PHP开发常用代码

常用python日期日志获取内容循环的代码片段