php常用设计模式应用场景及示例

单例模式


  • 含义描述

    应用程序中最多只有该类的一个实例存在

  • 应用场景

    常应用于数据库类设计,采用单例模式,只连接一次数据库,防止打开多个数据库连接。

  • 代码示例

    class Singleton {
        private static $instance; // 定义一个私有的静态变量保存类的实例
    
        private function __construct() {} // 将构造函数设为私有,防止外部直接创建对象
    
        public static function getInstance() {
            if (!self::$instance) { // 如果没有实例化则进行实例化操作
                self::$instance = new self();
            }
    
            return self::$instance; // 返回已经实例化好的对象
        }
    }
    
    // 调用单例模式获取实例
    $obj1 = Singleton::getInstance();
    $obj2 = Singleton::getInstance();
    
    var_dump($obj1 === $obj2); // true,两次获取到的都是同一个实例

工厂模式


  • 含义描述

    工厂设计模式常用于根据输入参数的不同或者应用程序配置的不同来创建一种专门用来实例化并返回其对应的类的实例

  • 应用场景

    1.后台数据导出:比如可导出文件类型有excel、csv、xml等,同样都是实现导出功能,但是要实例化的类是不同的,实现逻辑也不相同;

    2.支付系统:根据用户选择的支付方式实例化不同的支付类,比如积分、微信、支付宝等支付

    3.缓存功能:一般有文件缓存、session、cookie等,他们的实例也是不同的。

  • 代码示例

    <?php
    
    // 定义接口,定义所有对象的通用方法
    interface ShapeInterface
    {
        public function draw();
    }
    
    // 实现接口的具体类,画一个圆形
    class Circle implements ShapeInterface
    {
        public function draw()
        {
            echo "Drawing a circle.\n";
        }
    }
    
    // 实现接口的具体类,画一个长方形
    class Rectangle implements ShapeInterface
    {
        public function draw()
        {
            echo "Drawing a rectangle.\n";
        }
    }
    
    // 工厂类
    class ShapeFactory
    {
        // 根据传入的形状类型生成对应的对象
        public static function createShape($shapeType)
        {
            if ($shapeType == 'circle') {
                return new Circle();
            } elseif ($shapeType == 'rectangle') {
                return new Rectangle();
            } else {
                throw new Exception("Invalid shape type: " . $shapeType);
            }
        }
    }
    
    // 使用工厂类生成对象并调用方法
    try {
        $circle = ShapeFactory::createShape('circle');
        $circle->draw();
    
        $rectangle = ShapeFactory::createShape('rectangle');
        $rectangle->draw();
    
        // 尝试创建无效的形状类型
        $invalidShape = ShapeFactory::createShape('triangle');
    } catch (Exception $e) {
        echo "Error: " . $e->getMessage();
    }
    
    输出结果:
    Drawing a circle.
    Drawing a rectangle.
    Error: Invalid shape type: triangle

    在这个示例中,我们定义了一个ShapeInterface接口,它包含了一个draw()方法,用于绘制形状。然后,我们创建了两个实现该接口的具体类Circle和Rectangle,它们分别实现了draw()方法来绘制圆形和矩形。

    接下来,我们创建了一个ShapeFactory工厂类,它包含一个静态方法createShape(),该方法根据传入的形状类型(字符串)生成相应的对象实例。在createShape()方法中,我们根据传入的形状类型使用条件语句创建对应的对象,并返回该对象。如果传入的形状类型无效,我们将抛出一个异常。

    最后,我们使用ShapeFactory类来生成圆形和矩形的对象,并调用它们的draw()方法来绘制形状。同时,我们也尝试创建一个无效的形状类型(三角形),以演示异常处理机制。

策略模式


  • 含义描述

    策略设计模式是一种行为设计模式,它允许你在运行时改变对象的行为。在策略模式中,一个类的行为或其算法可以在运行时更改应用场景

  • 应用场景

    1. 商城促销方式:对秒杀、满减、打折等不同促销方式用不同的算法计算不同的结算价格
    2. 日志系统:根据日志的类型做不同的响应
  • 代码示例

    //我们将以一个简单的日志记录系统为例,其中我们可以选择不同的日志策略来记录信息。
    
    //首先,我们定义一个日志策略接口:
    
    <?php
    interface LoggingStrategy {
        public function log(string $message);
    }
    
    //然后,我们实现几种不同的日志策略,例如控制台日志、文件日志和数据库日志:
    
    <?php
    class ConsoleLog implements LoggingStrategy {
        public function log(string $message) {
            echo "Console: " . $message . PHP_EOL;
        }
    }
    
    class FileLog implements LoggingStrategy {
        private $file;
    
        public function __construct(string $file) {
            $this->file = $file;
        }
    
        public function log(string $message) {
            file_put_contents($this->file, "File: " . $message . PHP_EOL, FILE_APPEND);
        }
    }
    
    class DatabaseLog implements LoggingStrategy {
        private $pdo;
    
        public function __construct(PDO $pdo) {
            $this->pdo = $pdo;
        }
    
        public function log(string $message) {
            $stmt = $this->pdo->prepare("INSERT INTO logs (message) VALUES (?)");
            $stmt->execute([$message]);
        }
    }
    
    //接下来,我们创建一个Logger类,该类使用策略模式来记录日志:
    
    <?php
    class Logger {
        private $strategy;
    
        public function __construct(LoggingStrategy $strategy) {
            $this->strategy = $strategy;
        }
    
        public function setStrategy(LoggingStrategy $strategy) {
            $this->strategy = $strategy;
        }
    
        public function log(string $message) {
            $this->strategy->log($message);
        }
    }
    
    //最后,我们在主程序中使用Logger类:
    
    <?php
    // 创建一个PDO实例用于数据库连接(这里仅作示例,实际使用时需要配置数据库连接参数)
    $pdo = new PDO('mysql:host=localhost;dbname=mydb', 'username', 'password');
    
    // 创建不同的日志策略实例
    $consoleLog = new ConsoleLog();
    $fileLog = new FileLog('path/to/logfile.log');
    $databaseLog = new DatabaseLog($pdo);
    
    // 创建一个Logger实例,并设置初始的日志策略为控制台日志
    $logger = new Logger($consoleLog);
    $logger->log("This is a console log message.");
    
    // 更改日志策略为文件日志
    $logger->setStrategy($fileLog);
    $logger->log("This is a file log message.");
    
    // 更改日志策略为数据库日志
    $logger->setStrategy($databaseLog);
    $logger->log("This is a database log message.");
    
    在这个例子中,Logger类是一个上下文类,它使用LoggingStrategy接口来定义日志记录的行为。通过setStrategy方法,我们可以动态地更改日志记录的策略,从而在不同的场景下使用不同的日志记录方式。这样,如果我们想要添加新的日志策略,只需要实现LoggingStrategy接口,并在Logger中使用它即可,而不需要修改Logger类的代码。
  • 与工厂模式的区别
    1. 目的不同:工厂模式的目的是创建对象,根据传入的参数或条件返回不同类型的对象;而策略模式的目的是在运行时选择不同的行为或算法。
    2. 关注点不同:工厂模式关注对象的创建过程,封装了对象的创建逻辑;而策略模式关注行为或算法的选择和切换。
    3. 使用场景不同:工厂模式通常用于处理对象的创建问题,如多数据库选择、类库文件加载等;而策略模式通常用于处理算法或行为的选择问题,如排序算法的选择、支付方式的选择等。

观察者模式


  • 含义描述

    观察者模式是一种软件设计模式,属于对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

  • 应用场景

    1. 事件处理:观察者模式常被用于事件处理机制,如图形用户界面(GUI)框架中的按钮点击事件、窗口的打开和关闭事件等。观察者对象可以订阅这些特定事件,并在事件发生时接收通知并执行相应的操作。
    2. 消息通知:在消息通知系统中,如聊天应用、社交媒体平台等需要实时消息传递的场景中,观察者模式也得到了广泛应用。当发布者发布新消息时,订阅该消息的观察者会收到通知并进行相应的处理。
    3. 股票市场:在股票市场中,观察者模式可以用于实现订阅者监听发布者的状态变化并作出响应。
    4. 日志记录:观察者模式还可以用于实时日志记录系统。在这种情况下,日志记录器充当被观察者,而观察者可以是日志分析器、报警系统等。当日志发生变化时,观察者会收到通知并执行相应的操作,如生成报告、发送警报等。
    5. Redis中的发布订阅:Redis中的基于频道的发布订阅也是观察者模式的一个应用实例。
  • 代码示例

    PHP 中实现观察者模式,可以定义一个 Subject 类和一个 Observer 接口。Subject 类维护一个观察者列表,并提供添加、删除和通知观察者的方法。Observer 接口定义了观察者需要实现的方法。
    
    下面是一个简单的示例代码,展示了如何在 PHP 中实现观察者模式:
    
    <?php
    // Observer 接口
    interface Observer {
        public function update(Subject $subject);
    }
    
    // ConcreteObserver 类
    class ConcreteObserver implements Observer {
        private $name;
    
        public function __construct($name) {
            $this->name = $name;
        }
    
        public function update(Subject $subject) {
            echo "Observer {$this->name} received update: {$subject->getState()}\n";
        }
    }
    
    // Subject 类
    class Subject {
        private $observers = [];
        private $state;
    
        public function attach(Observer $observer) {
            $this->observers[] = $observer;
        }
    
        public function detach(Observer $observer) {
            $key = array_search($observer, $this->observers);
            if ($key !== false) {
                unset($this->observers[$key]);
            }
        }
    
        public function notify() {
            foreach ($this->observers as $observer) {
                $observer->update($this);
            }
        }
    
        public function setState($state) {
            $this->state = $state;
            $this->notify();
        }
    
        public function getState() {
            return $this->state;
        }
    }
    
    // 使用示例
    $subject = new Subject();
    
    $observer1 = new ConcreteObserver('Observer 1');
    $observer2 = new ConcreteObserver('Observer 2');
    
    $subject->attach($observer1);
    $subject->attach($observer2);
    
    $subject->setState('New state');
    
    $subject->detach($observer1);
    
    $subject->setState('Another state');
    
    在上面的示例中,我们定义了一个 Observer 接口,它包含了一个 update 方法,用于接收主题的状态更新。ConcreteObserver 类实现了 Observer 接口,并在 update 方法中打印接收到的更新信息。
    
    Subject 类维护了一个观察者列表($observers),并提供了 attach 方法用于添加观察者,detach 方法用于移除观察者,以及 notify 方法用于通知所有观察者。setState 方法用于设置主题的状态,并在状态改变时调用 notify 方法通知观察者。getState 方法用于获取当前的主题状态。
    
    在使用示例中,我们创建了一个主题对象 $subject,并将两个观察者对象 $observer1$observer2 添加到主题中。然后,我们通过调用 setState 方法改变主题的状态,并观察者的 update 方法将被调用,打印接收到的更新信息。最后,我们通过调用 detach 方法移除了一个观察者,并再次改变主题的状态,观察者的更新信息将不再打印。

装饰器模式


  • 含义描述

    装饰器设计模式是一种结构型设计模式,它允许动态地给一个对象添加一些额外的职责或功能

  • 应用场景

    1. 游戏开发:装备各种不同的武器装备增加各种属性,如攻击力、HP和防御等
    2. 点餐系统:根据用户选择的不同商品规格和配料,计算价格和满减优惠
  • 代码示例

    // 定义一个接口,表示被装饰的对象
    interface Coffee {
        public function getCost();
        public function getIngredients();
    }
    
    // 创建一个实现了Coffee接口的具体对象,即被装饰的对象
    class SimpleCoffee implements Coffee {
        public function getCost() {
            return 1.99;
        }
    
        public function getIngredients() {
            return "Coffee";
        }
    }
    
    // 创建一个加奶咖啡装饰器类,它包装了Coffee对象
    class CoffeeWithMilk extends SimpleCoffee implements Coffee {
        private $coffee;
    
        public function __construct(Coffee $coffee) {
            $this->coffee = $coffee;
        }
    
        public function getCost() {
            return $this->coffee->getCost() + 0.30;
        }
    
        public function getIngredients() {
            return $this->coffee->getIngredients() . ", Milk";
        }
    }
    
    // 创建一个加糖咖啡的装饰器类,它也为Coffee对象添加额外的行为
    class CoffeeWithSugar extends CoffeeWithMilk {
        public function getCost() {
            return parent::getCost() + 0.20;
        }
    
        public function getIngredients() {
            return parent::getIngredients() . ", Sugar";
        }
    }
    
    // 使用装饰器
    $coffee = new SimpleCoffee();
    echo "Cost: " . $coffee->getCost() . "\n";
    echo "Ingredients: " . $coffee->getIngredients() . "\n";
    
    $coffeeWithMilk = new CoffeeWithMilk($coffee);
    echo "Cost: " . $coffeeWithMilk->getCost() . "\n";
    echo "Ingredients: " . $coffeeWithMilk->getIngredients() . "\n";
    
    $coffeeWithSugar = new CoffeeWithSugar($coffeeWithMilk);
    echo "Cost: " . $coffeeWithSugar->getCost() . "\n";
    echo "Ingredients: " . $coffeeWithSugar->getIngredients() . "\n";
    
    输出结果:
    Cost: 1.99
    Ingredients: Coffee
    Cost: 2.29
    Ingredients: Coffee, Milk
    Cost: 2.79
    Ingredients: Coffee, Milk, Milk, Sugar

    在这个示例中,我们有一个Coffee接口和两个实现了该接口的类:SimpleCoffee(被装饰的对象)和CoffeeWithMilk、CoffeeWithSugar(装饰器类)。
    CoffeeWithMilk装饰器类包装了一个Coffee对象,并添加了牛奶和额外的成本。CoffeeWithSugar装饰器类进一步包装了CoffeeWithMilk对象,并添加了糖和额外的成本。
    通过链式使用装饰器,我们可以动态地构建具有不同功能和成本的咖啡。

迭代器模式


  • 含义描述

    迭代器设计模式允许你顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。通过使用迭代器,聚合对象与其迭代逻辑可以分离,从而使代码更加灵活和可扩展

  • 应用场景

    1. 操作链表、树、图:用迭代器模式可以让集合类隐藏这些复杂性,只暴露“next”、“hasNext”等基本迭代操作,这样既简化了外部对集合元素的操作,也保护了集合的封装性
    2. for、foreach循环:通过for语句直接遍历任何实现了迭代协议的对象,极大地简化了遍历过程,并且能够利用生成器模式实现惰性计算和流式处理
  • 代码示例

    //首先,定义一个迭代器接口(Iterator):
    
    interface Iterator {
        public function current();
        public function key();
        public function next();
        public function rewind();
        public function valid();
    }
    
    //然后,定义一个具体的迭代器类(ConcreteIterator),它实现了迭代器接口:
    
    class ConcreteIterator implements Iterator {
        private $data;
        private $position = 0;
    
        public function __construct(array $data) {
            $this->data = $data;
        }
    
        public function current() {
            return $this->data[$this->position];
        }
    
        public function key() {
            return $this->position;
        }
    
        public function next() {
            $this->position++;
        }
    
        public function rewind() {
            $this->position = 0;
        }
    
        public function valid() {
            return isset($this->data[$this->position]);
        }
    }
    
    //接下来,定义一个聚合对象接口(Aggregate),该接口定义了一个方法来创建迭代器:
    
    interface Aggregate {
        public function createIterator();
    }
    
    //然后,创建一个实现了聚合对象接口的具体聚合类(ConcreteAggregate):
    
    class ConcreteAggregate implements Aggregate {
        private $data = [];
    
        public function add($item) {
            $this->data[] = $item;
        }
    
        public function createIterator() {
            return new ConcreteIterator($this->data);
        }
    }
    
    //最后,你可以使用这些类来实现迭代器的功能:
    
    // 创建一个聚合对象
    $aggregate = new ConcreteAggregate();
    $aggregate->add('Element 1');
    $aggregate->add('Element 2');
    $aggregate->add('Element 3');
    
    // 获取聚合对象的迭代器
    $iterator = $aggregate->createIterator();
    
    // 使用迭代器遍历聚合对象中的元素
    $iterator->rewind();
    while ($iterator->valid()) {
        echo $iterator->current() . "\n";
        $iterator->next();
    }

    在上面的示例中,ConcreteAggregate 类存储了一个元素数组,并提供了 createIterator() 方法来创建一个新的 ConcreteIterator 实例。ConcreteIterator 类实现了 Iterator 接口,用于遍历 ConcreteAggregate 中的元素。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!