设计模式二

设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,其中最出名的当属 Gang of Four (GoF) 的分类了,他们将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式、结构型模式和行为型模式

1单例模式

比如说我们在系统运行时候,就需要加载一些配置和属性,这些配置和属性是一定存在了,又是公共的,同时需要在整个生命周期中都存在,所以只需要一份就行,这个时候如果需要我再需要的时候new一个,显然是浪费内存并且再赋值没什么意义,所以这个时候我们就需要单例模式。

  • 场景为:Cache的连接,日志的写入。
    单例模式的作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个。

  • 怎么实现:

    • 私有的静态属性
    • 私有的构造函数,不允许外部实例化它
    • 类自己创建唯一的实例,并保存到全局属性当中
    • 提供公共的静态方法获取这个唯一的实例
    • 不允许克隆 ,私有的克隆方法
    • 不允许被反序列化。
      
      class Singleton {
      private static $instance = null;

    public static function getInstance()
    {
    if (!isset(self::$instance)) {
    self::$instance = new self;
    }
    return self::$instance;
    }

    private function __construct(){}

    private function __clone(){}

    private function __wakeup(){}
    }

2工厂模式

工厂设计模式常用于根据输入参数的不同或者应用程序配置的不同来创建一种专门用来实例化并返回其对应的类的实例。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式属于创建型模式。主要解决接口选择的问题。
工厂模式大概有三种:

  • 简单工厂模式
  • 模式定义: 简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,之所以可以这么说,是因为简单工厂模式是通过一个静态方法来创建对象的。它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
class FoodFactory {
    public makeFood(String name) {
        if (name.equals("noodle")) {
            Food noodle = new LanZhouNoodle();
            noodle.addSpicy("more");
            return noodle;
        } else if (name.equals("chicken")) {
            Food chicken = new HuangMenChicken();
            chicken.addCondiment("potato");
            return chicken;
        } else {
            return null;
        }
    }
}

其中,LanZhouNoodle 和 HuangMenChicken 都继承自 Food。

简单地说,简单工厂模式通常就是这样,一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。
我们强调职责单一原则,一个类只提供一种功能,FoodFactory 的功能就是只要负责生产各种 Food。

  • 适用环境:
    工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
    客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数
    简单工厂的作用是实例化对象,而不需要客户了解这个对象属于哪个具体的子类。
    简单工厂实例化的类具有相同的接口或者基类,在子类比较固定并不需要扩展时,可以使用简单工厂。

工厂方法模式 (多个类)

工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。
工厂父类负责定义创建产品对象的公共接口,让子类决定将哪一个类实例化,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。。它和简单工厂的区别是将对象的创建抽象成为一个接口,以解决简单工厂模式中的封闭开放原则。

public interface FoodFactory {
    Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {

    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new ChineseFoodA();
        } else if (name.equals("B")) {
            return new ChineseFoodB();
        } else {
            return null;
        }
    }
}
public class AmericanFoodFactory implements FoodFactory {

    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new AmericanFoodA();
        } else if (name.equals("B")) {
            return new AmericanFoodB();
        } else {
            return null;
        }
    }
}

其中,ChineseFoodA、ChineseFoodB、AmericanFoodA、AmericanFoodB 都派生自 Food。

客户端调用:

public class APP {
    public static void main(String[] args) {
        // 先选择一个具体的工厂
        FoodFactory factory = new ChineseFoodFactory();
        // 由第一步的工厂产生具体的对象,不同的工厂造出不一样的对象
        Food food = factory.makeFood("A");
    }
}

虽然都是调用 makeFood("A") 制作 A 类食物,但是,不同的工厂生产出来的完全不一样。
第一步,我们需要选取合适的工厂,然后第二步基本上和简单工厂一样。
核心在于,我们需要在第一步选好我们需要的工厂。比如,我们有 LogFactory 接口,实现类有 FileLogFactory 和 KafkaLogFactory,分别对应将日志写入文件和写入 Kafka 中,显然,我们客户端第一步就需要决定到底要实例化 FileLogFactory 还是 KafkaLogFactory,这将决定之后的所有的操作。

虽然简单,不过我也把所有的构件都画到一张图上:

  • 二者区别
    【工厂方法模式与简单工厂模式】
    工厂方法模式与简单工厂模式再结构上的不同不是很明显。
    工厂方法类的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上。
    工厂方法模式之所以有一个别名叫多态性工厂模式是因为具体工厂类都有共同的接口,或者有共同的抽象父类。
    当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了”开放-封闭”原则。而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。
    工厂方法模式退化后可以演变成简单工厂模式。

抽象工厂模式

抽象工厂提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
提供获取某个对象实例的一个接口,同时使调用代码避免确定实例化基类的步骤,将系统和实现细节分离开。

一个经典的例子是造一台电脑。我们先不引入抽象工厂模式,看看怎么实现。
因为电脑是由许多的构件组成的,我们将 CPU 和主板进行抽象,然后 CPU 由 CPUFactory 生产,主板由 MainBoardFactory 生产,然后,我们再将 CPU 和主板搭配起来组合在一起,如下图:

这个时候的客户端调用是这样的:
// 得到 Intel 的 CPU
CPUFactory cpuFactory = new IntelCPUFactory();
CPU cpu = intelCPUFactory.makeCPU();

// 得到 AMD 的主板
MainBoardFactory mainBoardFactory = new AmdMainBoardFactory();
MainBoard mainBoard = mainBoardFactory.make();

// 组装 CPU 和主板
Computer computer = new Computer(cpu, mainBoard);

单独看 CPU 工厂和主板工厂,它们分别是前面我们说的工厂模式。这种方式也容易扩展,因为要给电脑加硬盘的话,只需要加一个 HardDiskFactory 和相应的实现即可,不需要修改现有的工厂。

但是,这种方式有一个问题,那就是如果 Intel 家产的 CPU 和 AMD 产的主板不能兼容使用,那么这代码就容易出错,因为客户端并不知道它们不兼容,也就会错误地出现随意组合。

  • 简单工厂模式(静态方法工厂模式) : 用来生产同一等级结构中的任意产品。(不能增加新的产品)
  • 工厂模式 :用来生产同一等级结构中的固定产品。(支持增加任意产品)
  • 抽象工厂 :用来生产不同产品种类的全部产品。(不能增加新的产品,支持增加产品种类)

1 工厂方法针对每一种产品提供一个工厂类,通过不同的工厂实例来创建不同的产品实例,在同一等级结构中,支持增加任意产品。

2 抽象工厂是应对产品族概念的,比如说,每个电脑公司可能要同时生产cpu,主板,硬盘,那么每一个工厂都要有创建这些产品的方法。应对产品族概念而生,增加新的产品线很容易,但是无法增加新的产品。

配图并加以说明:
产品族的概念,它代表了组成某个产品的一系列附件的集合

当涉及到这种产品族的问题的时候,就需要抽象工厂模式来支持了。我们不再定义 CPU 工厂、主板工厂、硬盘工厂、显示屏工厂等等,我们直接定义电脑工厂,每个电脑工厂负责生产所有的设备,这样能保证肯定不存在兼容问题

public static void main(String[] args) {
    // 第一步就要选定一个“大厂”
    ComputerFactory cf = new AmdFactory();
    // 从这个大厂造 CPU
    CPU cpu = cf.makeCPU();
    // 从这个大厂造主板
    MainBoard board = cf.makeMainBoard();
      // 从这个大厂造硬盘
      HardDisk hardDisk = cf.makeHardDisk();

    // 将同一个厂子出来的 CPU、主板、硬盘组装在一起
    Computer result = new Computer(cpu, board, hardDisk);
}

抽象工厂的问题也是显而易见的,比如我们要加个显示器,就需要修改所有的工厂,给所有的工厂都加上制造显示器的方法。这有点违反了对修改关闭,对扩展开放这个设计原则。

3 策略模式

策略模式(Strategy)属于行为型模式。
(行为型模式关注的是各个类之间的相互作用,将职责划分清楚,使得我们的代码更加地清晰)
),又叫算法簇模式,就是定义了不同的算法族,并且之间可以互相替换,

  • 此模式让算法的变化独立于使用算法的客户。
  • 是为了解决的是策略的切换与扩展,更简洁的说是定义策略族,分别封装起来,让他们之间可以相互替换,同一个对象调用同一个方法,结果不同。
  • 常见的使用场景
    • 单元测试中,我们可以在文件和内存存储之间进行切换

策略模式适用于当一个应用程序需要实现一种特定的服务或者功能,而且该程序有多种实现方式时使用。

场景是,我们需要画一个图形,可选的策略就是用红色笔、绿色笔或者蓝色笔来画。
首先,先定义一个策略接口:

public interface Strategy {
   public void draw(int radius, int x, int y);
}
//然后我们定义具体的几个策略:
public class RedPen implements Strategy {
   @Override
   public void draw(int radius, int x, int y) {
      System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
   }
}
public class GreenPen implements Strategy {
   @Override
   public void draw(int radius, int x, int y) {
      System.out.println("用绿色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
   }
}
public class BluePen implements Strategy {
   @Override
   public void draw(int radius, int x, int y) {
      System.out.println("用蓝色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
   }
}

使用策略类

public class Context {
   private Strategy strategy;

   public Context(Strategy strategy){
      this.strategy = strategy;
   }    

   public int executeDraw(int radius, int x, int y){
      return strategy.draw(radius, x, y);
   }
}

client调用的时候

public static void main(String[] args) {
    Context context = new Context(new BluePen()); // 使用绿色笔来画
      context.executeDraw(10, 0, 0);
}
  • 与工厂模式的区别
    • 相似:
      在结构上相似,都是对象的继承和多态,所以非常难区分。
    • 不同:
      • 1 用途不同:
        抽象工厂是对对象的管理,它的作用就是创建不同的对象;
      • 2 方式不同:
        工厂模式是创建型的设计模式,它接受指令,创建出符合要求的实例。它主要解决的是资源的统一分发,将对象的创建完全独立出来,不同的对象对用同一个方法,结果不同。

4 观察者模式

  • 观察者模式有时也被称作发布/订阅模式,该模式用于为对象实现发布/订阅功能:
    一旦主体对象状态发生改变,与之关联的观察者对象会收到通知,并进行相应操作。
  • 将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。
  • 我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。
  • 观察者就是解决这类的耦合关系的。 消息队列系统、事件都使用了观察者模式。

PHP 为观察者模式定义了两个接口:SplSubject 和 SplObserver。SplSubject 可以看做主体对象的抽象,SplObserver 可以看做观察者对象的抽象,要实现观察者模式,只需让主体对象实现 SplSubject ,观察者对象实现 SplObserver,并实现相应方法即可。

观察者模式解除了主体和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。

首先,需要定义主题,每个主题需要持有观察者列表的引用,用于在数据变更的时候通知各个观察者:

public class Subject {
    private List<Observer> observers = new ArrayList<Observer>();
    private int state;
    public int getState() {
        return state;
    }
    public void setState(int state) {
        this.state = state;
        // 数据已变更,通知观察者们
        notifyAllObservers();
    }
    // 注册观察者
    public void attach(Observer observer) {
        observers.add(observer);
    }
    // 通知观察者们
    public void notifyAllObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

定义观察者接口:

public abstract class Observer {
    protected Subject subject;
    public abstract void update();
}

,通常场景下,既然用到了观察者模式,我们就是希望一个事件出来了,会有多个不同的类需要处理相应的信息。比如,订单修改成功事件,我们希望发短信的类得到通知、发邮件的类得到通知、处理物流信息的类得到通知等。
我们来定义具体的几个观察者类:

public class BinaryObserver extends Observer {
    // 在构造方法中进行订阅主题
    public BinaryObserver(Subject subject) {
        this.subject = subject;
        // 通常在构造方法中将 this 发布出去的操作一定要小心
        this.subject.attach(this);
    }
    // 该方法由主题类在数据变更的时候进行调用
    @Override
    public void update() {
        String result = Integer.toBinaryString(subject.getState());
        System.out.println("订阅的数据发生变化,新的数据处理为二进制值为:" + result);
    }
}

public class HexaObserver extends Observer {
    public HexaObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }
    @Override
    public void update() {
        String result = Integer.toHexString(subject.getState()).toUpperCase();
        System.out.println("订阅的数据发生变化,新的数据处理为十六进制值为:" + result);
    }
}

客户端使用也非常简单:

public static void main(String[] args) {
    // 先定义一个主题
    Subject subject1 = new Subject();
    // 定义观察者
    new BinaryObserver(subject1);
    new HexaObserver(subject1);

    // 模拟数据变更,这个时候,观察者们的 update 方法将会被调用
    subject.setState(11);
}

输出结果output:

  • 订阅的数据发生变化,新的数据处理为二进制值为:1011
  • 订阅的数据发生变化,新的数据处理为十六进制值为:B

5 门面模式

门面模式(Facade)又称外观模式,用于为子系统中的一组接口提供一个一致的界面。
门面模式定义了一个高层接口,这个接口使得子系统更加容易使用:引入门面角色之后,用户只需要直接与门面角色交互,用户与子系统之间的复杂关系由门面角色来实现,从而降低了系统的耦合度。

门面模式对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便;
实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的,松耦合关系使得子系统的组件变化不会影响到它的客户;
如果应用需要,门面模式并不限制客户程序使用子系统类,因此你可以让客户程序在系统易用性和通用性之间加以选择。 Laravel 中门面模式的使用也很广泛,基本上每个服务容器中注册的服务提供者类都对应一个门面类。

优点:1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。

下面,我们看看怎么用门面模式来让客户端调用更加友好一些。
我们先定义一个门面:

public class ShapeMaker {
   private Shape circle;
   private Shape rectangle;
   private Shape square;

   public ShapeMaker() {
      circle = new Circle();
      rectangle = new Rectangle();
      square = new Square();
   }

  /**
   * 下面定义一堆方法,具体应该调用什么方法,由这个门面来决定
   */

   public void drawCircle(){
      circle.draw();
   }
   public void drawRectangle(){
      rectangle.draw();
   }
   public void drawSquare(){
      square.draw();
   }
}

看看现在客户端怎么调用:

public static void main(String[] args) {
  ShapeMaker shapeMaker = new ShapeMaker();

  // 客户端调用现在更加清晰了
  shapeMaker.drawCircle();
  shapeMaker.drawRectangle();
  shapeMaker.drawSquare();        
}

看到这是不是觉的也没啥?没有对比就没有伤害,来看下不用门面怎么写

首先,我们定义一个接口:

public interface Shape {
   void draw();
}
定义几个实现类:
public class Circle implements Shape {
    @Override
    public void draw() {
       System.out.println("Circle::draw()");
    }
}

public class Rectangle implements Shape {
    @Override
    public void draw() {
       System.out.println("Rectangle::draw()");
    }
}

客户端调用:
public static void main(String[] args) {
    // 画一个圆形
      Shape circle = new Circle();
      circle.draw();

      // 画一个长方形
      Shape rectangle = new Rectangle();
      rectangle.draw();
}

以上是我们常写的代码,我们需要画圆就要先实例化圆,画长方形就需要先实例化一个长方形,然后再调用相应的 draw() 方法

门面模式的优点显而易见,客户端不再需要关注实例化时应该使用哪个实现类,直接调用门面提供的方法就可以了,因为门面类提供的方法的方法名对于客户端来说已经很友好了。

6 责任链模式

责任链通常需要先建立一个单向链表,然后调用方只需要调用头部节点就可以了,后面会自动流转下去。
比如流程审批就是一个很好的例子,只要终端用户提交申请,根据申请的内容信息,自动建立一条责任链,然后就可以开始流转了。

  • 场景,用户参加一个活动可以领取奖品,但是活动需要进行很多的规则校验然后才能放行
  • 比如首先需要校验用户是否是新用户、今日参与人数是否有限额、全场参与人数是否有限额等等。
  • 设定的规则都通过后,才能让用户领走奖品。
  • JS 中的事件冒泡

如果产品给你这个需求的话,我想大部分人一开始肯定想的就是,用一个 List 来存放所有的规则,然后 foreach 执行一下每个规则就好了。不过,读者也先别急,看看责任链模式和我们说的这个有什么不一样?
首先,我们要定义流程上节点的基类:

// 词汇过滤链条
abstract class FilterChain
{
    protected $next;
    public function setNext($next)
    {
        $this->next = $next;
    }
    abstract public function filter($message);
}

// 严禁词汇
class FilterStrict extends FilterChain
{
    public function filter($message)
    {
        foreach (['枪X', '弹X', '毒X'] as $v) {
            if (strpos($message, $v) !== false) {
                throw new \Exception('该信息包含敏感词汇!');
            }
        }
        if ($this->next) {
            return $this->next->filter($message);
        } else {
            return $message;
        }
    }
}

// 警告词汇
class FilterWarning extends FilterChain
{
    public function filter($message)
    {
        $message = str_replace(['打架', '丰胸', '偷税'], '*', $message);
        if ($this->next) {
            return $this->next->filter($message);
        } else {
            return $message;
        }
    }
}

// 手机号加星
class FilterMobile extends FilterChain
{
    public function filter($message)
    {
        $message = preg_replace("/(1[3|5|7|8]\d)\d{4}(\d{4})/i", "$1****$2", $message);
        if ($this->next) {
            return $this->next->filter($message);
        } else {
            return $message;
        }
    }
}

$f1 = new FilterStrict();
$f2 = new FilterWarning();
$f3 = new FilterMobile();

$f1->setNext($f2);
$f2->setNext($f3);

$m1 = "现在开始测试链条1:语句中不包含敏感词,需要替换掉打架这种词,然后给手机号加上星:13333333333,这样的数据才可以对外展示哦";
echo $f1->filter($m1);
echo PHP_EOL;

$m2 = "现在开始测试链条2:这条语句走不到后面,因为包含了毒X,直接就报错了!!!语句中不包含敏感词,需要替换掉打架这种词,然后给手机号加上星:13333333333,这样的数据才可以对外展示哦";
echo $f1->filter($m2);
echo PHP_EOL;

代码其实很简单,就是先定义好一个链表,然后在通过任意一节点后,如果此节点有后继节点,那么传递下去。

7 享元模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。我们将通过创建 5 个对象来画出 20 个分布于不同位置的圆来演示这种模式。由于只有 5 种可用的颜色,所以 color 属性被用来检查现有的 Circle 对象

  • 使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。
  • 优点:大大减少对象的创建,降低系统的内存,使效率提高。
  • 
    <?php
    /***
    * 享元模式
    * Class Flyweight
    */

abstract class Resources{
public $resource=null;

abstract public function operate();

}

class unShareFlyWeight extends Resources{
public function __construct($resource_str) {
$this->resource = $resource_str;
}

public function operate(){
    echo $this->resource."<br>";
}

}

class shareFlyWeight extends Resources{
private $resources = array();

public function get_resource($resource_str){
    if(isset($this->resources[$resource_str])) {
        return $this->resources[$resource_str];
    }else {
        return $this->resources[$resource_str] = $resource_str;
    }
}

public function operate(){
    foreach ($this->resources as $key => $resources) {
        echo $key.":".$resources."<br>";
    }
}

}

// client
$flyweight = new shareFlyWeight();
$flyweight->get_resource('a');
$flyweight->operate();

$flyweight->get_resource('b');
$flyweight->operate();

$flyweight->get_resource('c');
$flyweight->operate();

// 不共享的对象,单独调用
$uflyweight = new unShareFlyWeight('A');
$uflyweight->operate();

$uflyweight = new unShareFlyWeight('B');
$uflyweight->operate();

//结果
//a:a
//a:a
//b:b
//a:a
//b:b
//c:c
//A
//B


### 8 装饰器模式(Decorator)
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

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

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

<?php
interface Component {
public function operation();
}

abstract class Decorator implements Component{ // 装饰角色
protected $_component;
public function __construct(Component $component) {
$this->_component = $component;
}
public function operation() {
$this->_component->operation();
}
}

class ConcreteDecoratorA extends Decorator { // 具体装饰类A
public function construct(Component $component) {
parent::
construct($component);
}
public function operation() {
parent::operation(); // 调用装饰类的操作
$this->addedOperationA(); // 新增加的操作
}
public function addedOperationA() {echo 'A加点酱油;';}
}

class ConcreteDecoratorB extends Decorator { // 具体装饰类B
public function construct(Component $component) {
parent::
construct($component);
}
public function operation() {
parent::operation();
$this->addedOperationB();
}
public function addedOperationB() {echo "B加点辣椒;";}
}

class ConcreteComponent implements Component{ //具体组件类
public function operation() {}
}

// clients
$component = new ConcreteComponent();
$decoratorA = new ConcreteDecoratorA($component);
$decoratorB = new ConcreteDecoratorB($decoratorA);

$decoratorA->operation();//输出:A加点酱油;
echo '<br>--------<br>';
$decoratorB->operation();//输出:A加点酱油;B加点辣椒;

就是在不侵入原方法的代码的情况下,扩展功能

### 9 命令模式
将一个请求封装为一个对象,从而使用户可用不同的请求对客户进行参数化。对请求排队或记录请求日志,以及支持撤销的操作。

命令模式以松散耦合主题为基础,发送消息、命令和请求,或通过一组处理程序发送任意内容。
每个处理程序都会自行判断自己能否处理请求。
如果可以,该请求被处理,进程停止。
您可以为系统添加或移除处理程序,而不影响其他处理程序。

interface ICommand {
function onCommand($name, $args);
}

class CommandChain {
private $_commands = array();
public function addCommand($cmd) {
$this->_commands []= $cmd;
}

public function runCommand($name, $args) {
    foreach($this->_commands as $cmd) {
        if ($cmd->onCommand($name, $args)) return;
    }
}

}

class UserCommand implements ICommand {
public function onCommand($name, $args) {
if ($name != 'addUser') return false;
echo("UserCommand handling 'addUser'\n");
return true;
}
}

class MailCommand implements ICommand {
public function onCommand($name, $args) {
if ($name != 'mail') return false;
echo("MailCommand handling 'mail'\n");
return true;
}
}

$cc = new CommandChain();
$cc->addCommand(new UserCommand());
$cc->addCommand(new MailCommand());
$cc->runCommand('addUser', null);
$cc->runCommand('mail', null);


责任链模式是解耦请求发送者和处理者,因为处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

命令模式每个处理程序都会自行判断自己能否处理请求。

### 10 适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。

/**

  • 第一种方式:对象适配器
    */
    interface Target {
    public function sampleMethod1();
    public function sampleMethod2();
    }

class Adaptee {
public function sampleMethod1() {
echo '++++++++';
}
}

class Adapter implements Target {
private $_adaptee;

public function __construct(Adaptee $adaptee) {
    $this->_adaptee = $adaptee;
}

public function sampleMethod1() {
    $this->_adaptee->sampleMethod1();
}

public function sampleMethod2() {
    echo '————————';
}

}
$adapter = new Adapter(new Adaptee());
$adapter->sampleMethod1();//输出:++++++++
$adapter->sampleMethod2();//输出:————————

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注