2025-04-14

参与者

  1. 表达式接口:这是我们的表达式的蓝图,定义了一种解释不同类型表达式的方法。
  2. 终端表达式:这些是返回值的主要表达式。
  3. 非终端表达式:这些表达式表示操作并使用终端表达式来完成其任务

 

标签: 设计模式
2025-04-14

工厂方法模式在 PHP 代码中被广泛使用。当您需要为代码提供高度灵活性时,它非常有用。

案例:手机加工

定义interface

interface PhoneProcessInterface {
  public function process($phone_info);
}

 

定义各种手机加工的厂商

class IphoneProcess implements PhoneProcessInterface {
  public function process($phone_info)
  {
    echo 'iphone process....';
  }
}

class HuaweiProcess implements PhoneProcessInterface {
  public function process($phone_info)
  {
    echo 'Huawei process....';
  }
}

class SamsungProcess implements PhoneProcessInterface {
  public function process($phone_info)
  {
    echo "SamSung process...";
  }
}

定义工厂会帮助实例化. 外部直接使用

class PhoneFactory {
  public function samsung($phone_info) {
    return (new SamsungProcess())->process($phone_info);
  }

  public function huawei($phone_info) {
    return (new HuaweiProcess())->process($phone_info);
  }


  public function iphone($phone_info) {
    return (new IphoneProcess())->process($phone_info);
  }
}

测试

$phone_info = [
  'name' => 'iphone',
  'version' => '15'
];
$phone_factory = new PhoneFactory();
if ($phone_info['name'] == 'iphone') {
  $phone_factory->iphone($phone_info);
}

 

输出Iphone process

 

标签: 设计模式
2025-04-14

单例

单例很简单就是一个类一个类只初始化一次

 

定义一个单例类

class Single {
  protected static $instance = NULL;

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

 

继承和使用

class DB extends Single {
}

class Logger extends Single {

}

$db = DB::getInstance();
$logger = Logger::getInstance();

 

多例

如果我们需要的不仅仅是一个记录器实例,因为有些消息必须写入文件中,有些消息需要通过电子邮件发送

定义多例

class Multiple {
  protected static array $instance = [];

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

 

// 定义写入日志的接口
interface LoggerHanderInterface {
  public function save($level, $message, $context);
}

 

定义3种写入日志的方式

class DbLogger implements LoggerHanderInterface {
  public function save($level, $message, $context) {
    print ("Save to db $level: $message");
  }
}

class FileLogger implements LoggerHanderInterface {
  public function save($level, $message, $context) {
    print ("Save File$level: $message");
  }
}

class MailLogger implements LoggerHanderInterface {
  public function save($level, $message, $context) {
    print ("Send mail $level: $message");
  }
}

 

最后定义logger. 这里使用了psr\Log这个包.

class Logger extends Multiple implements \Psr\Log\LoggerInterface {
  use \Psr\Log\LoggerTrait;

  private LoggerHanderInterface $handle;

  public function setHandle(LoggerHanderInterface $hander) {
    $this->handle = $hander;
  }

  public function log($level, $message, array $context = array())
  {
    $this->handle->save($level, $message, $context);
  }
}

 

测试

$db_logger = Logger::getInstance('db');
$db_logger->setHandle(new DbLogger());

$file_logger = Logger::getInstance('file');
$file_logger->setHandle(new FileLogger());

$mail_logger = Logger::getInstance('mail');
$mail_logger->setHandle(new MailLogger());


// 打印hander 是 DbLogger
$logger1 = Logger::getInstance('db');
var_dump($logger1);

// 打印hander 是 DbLogger
$logger2 = Logger::getInstance('db');
var_dump($logger2);

// 打印hander 是 MailLogger
$logger3 = Logger::getInstance('mail');
var_dump($logger3);

 

标签: 设计模式
2025-04-14

当你需要操作别人遗留下来的项目,或者说第三方的代码的时候。尤其是通常情况下,这些代码你不容易去重构它们,也没有提供测试(tests)。这个时候,你就可以创建一个facade(“外观”),去将原来的代码“包裹”起来,以此来简化或优化其使用场景。

原文

 

标签: 设计模式
2025-04-14

当一个对象或者数据有多种展示方式的时候用装饰者模式.  比如数组可以以json, xml的展示方式. 再比如用户数据A页面会有一种展示方式. B页面会有一种展示方式

 

数组装饰

现在来用装饰者模式展示array. 先定义一个对象用来接受数组和输出输出.

class OutputData {

    private DecoratorInterface $decorator;

    public function __construct(private array $data) {}

    public function setDecorator(DecoratorInterface $decorator) {
        $this->decorator = $decorator;
    }

    public function output() {
        return $this->decorator->renderData($this->data);
    }

}

 

定义装饰者, 分别定义JSON和XML

 

interface DecoratorInterface {
    public function renderData($data);
}

class XMLRender implements DecoratorInterface {
    function toXml(SimpleXMLElement $object, array $data) {
        foreach ($data as $key => $value) {
            // if the key is an integer, it needs text with it to actually work.
            $valid_key  = is_numeric($key) ? "key_$key" : $key;
            $new_object = $object->addChild(
                $valid_key,
                is_array($value) ? null : htmlspecialchars($value)
            );

            if (is_array($value)) {
                $this->toXml($new_object, $value);
            }
        }
    }
    public function renderData($data) {
        $xml = new SimpleXMLElement('<root/>');
        $this->toXml($xml, $data);
        return $xml->saveXML();
    }
}

class JsonRender implements DecoratorInterface {
    public function renderData($data) {
        return json_encode($data);
    }
}

 

最后根据所需输出指定类型

$data = new OutputArray([
    'books' => [
        'Abook',
        'Bbook',
        'Cbook'
    ],
    'cart' => [
        'Acart',
        'Bcart',
        'Ccart'
    ]
]);
$data->setDecorator(new XMLRender());
$data->setDecorator(new JsonRender());
print_r($data->output());

 

商品装饰

定义菜品接口

interface FoodItem {
    // 定义附加的成本.
    public function cost();
}

 

// 定义一个手抓饼. 成本为10块钱.
class HandCake implements FoodItem {
    private array  $items = [];
    public function cost() {
        return 10;
    }

    public function addItem(FoodItem $item) {
        $this->items[] = $item;
    }

    public function Calculation() {
        $total = $this->cost();
        foreach ($this->items as $item) {
            $total += $item->cost();
        }
        return $total;
    }
}

 

定义小菜可选增加

// 定义鸡蛋小菜的成品为4块
class Egg implements FoodItem {
    public function cost () {
        return 4;
    }
}

// 定义一个火腿价格为6块
class Ham implements FoodItem {
    public function cost () {
        return 6;
    }
}

$handcake = new HandCake();
$handcake->addItem(new Egg());
$handcake->addItem(new Egg());
$handcake->addItem(new Egg());
$handcake->addItem(new Ham());
$handcake->addItem(new Ham());
print $handcake->Calculation();

产品活动装饰

定义一个商品类

class Product {
    public string $title;
    public float $price;
}

定义活动接口

interface ProductActivities {
    public function getReduce(Product $product);
}

定义活动

// 双十一减5块.
class Two11 implements ProductActivities {
    public function getReduce($product) {
        return 5;
    }
}

// 满500减50.
class Filled500 implements ProductActivities {
    public function getReduce(Product $product) {
        if ($product->price > 500) {
            return 50;
        }
    }
}

 

定义产品建造者来获取产品

class ProductBuilder {
    private Product $product;
    private array $activities;

    public function __construct($title, $price) {
        $product = new Product();
        $product->title = $title;
        $product->price = $price;
        $this->product = $product;
    }

    /**
     * 获取商品.
     *
     * @return mixed
     */
    public function getProduct() {
        // 根据活动重新计算减少的价格.
        $price = $this->product->price;
        foreach ($this->activities as $activity) {
            $price -= (int)$activity->getReduce($this->product);
        }
        $this->product->price = $price;
        return $this->product;
    }

    /**
     * 添加活动.
     *
     * @param $activity
     */
    public function addActivity($activity) {
        $this->activities[] = $activity;
    }
}

最后测试

$product = new ProductBuilder('Iphone16', '9000');
$product->addActivity(new Two11());
$product->addActivity(new Filled500());
print_r($product->getProduct());

 

标签: 设计模式
2025-04-14
//建造者模式,目的是消除其它对象复杂的创建过程
/* 描述一个用户的类,包含用户姓名,年龄,金钱 */
class UserInfo {
    protected $userName = ''; 
    protected $userAge = '';
    protected $userMoney = '';
    public function setUserName($userName) {
        $this->userName = $userName;
    }
    public function setUserAge($userAge) {
        $this->userAge = $userAge;
    }
    public function setUserMoney($userMoney) {
        $this->userMoney = $userMoney;
    }
    public function getPeople() {
        echo "这个人的姓名是:" . $this->setUserName . ',年龄是:' . $this->userAge . ', 金钱:' . $this->userMoney;
    }
}
/* 实例化,并且创建这个用户的时候,是很痛苦的,需要设置用户名,年龄和金钱*/
$peopleInfo = array(
    'userName' =  'initphp',
    'userAge' =  28,
    'userMoney' =  '100元'
    );
$UserInfo = new UserInfo;
//下面需要一步步的设置用户信息,才能得到用户详细信息,过程纠结而痛苦
$UserInfo->setUserName($peopleInfo['userName']); 
$UserInfo->setUserAge($peopleInfo['userAge']);
$UserInfo->setUserMoney($peopleInfo['userMoney']);
$UserInfo->getPeople();

 

//UserInfoBuilder 用户信息建造者类,将UserInfo的创建过程封装掉,开发者使用起来心情舒畅
<?php
//建造者模式,目的是消除其它对象复杂的创建过程
include("UserInfo.php");
class UserInfoBuilder {
    protected $obj;
    public function __construct() {
        $this->obj = new UserInfo;
    }
    public function buildPeople($peopleInfo) {
        $this->obj->setUserName($peopleInfo['userName']);
        $this->obj->setUserAge($peopleInfo['userAge']);
        $this->obj->setUserMoney($peopleInfo['userMoney']);
    } 
    public function getPeople() {
        $this->obj->getPeople();
    }
}
/* 创建过程被封装了,用户使用简单了 */
$peopleInfo = array(
    'userName' =  'initphp',
    'userAge' =  28,
    'userMoney' =  '100元'
    );
$UserInfoBuilder = new UserInfoBuilder;
$UserInfoBuilder->buildPeople($peopleInfo); //直接一个build
$UserInfoBuilder->getPeople();

文章来源于: https://cloud.tencent.com/developer/article/1723155

 

标签: 设计模式
2025-04-14

适配器模式就是保留现有类所提供的功能和服务 在不改变原有系统的基础上提供新的接口服务. 比如苹果笔记本并不支持直接插入网线. 而想要插入网线就需要买一个适配器. 这个适配器可以只支持插入网线也可以是功能强大的支持各种插口

使用场景如下:

  1. 一直调用系统的一个服务. 随着服务升级里面的一些函数改调用方法或者直接移掉了
  2. 你想使用一个已经存在的类,而它的接口不符合你的需求。
  3. 你可能正从事一项新的项目,它要用到新版本的库所带来的特性,但你已经拥有许多旧的应用程序,并且它们与以前旧版本的库交互运行地很好

 

 

标签: 设计模式
2025-04-14

需求.

  1. 用户登陆成功根据用户的专业给用户推送产品.
  2. 用户登陆成功给用户发送一封邮件
  3. 用户登陆成功判断后一次登陆时间如果超过1个月告诉通知营销部门
  4. 用户登陆成功如果角色是超级管理员自动跳转到 /admin/contents 页面
  5. 用户登陆成功如果角色是网站管理员自动跳转到 /manage页面

 

先定义一个模块来完成我们的需求. 模块名为.

普通的做法

use Drupal\user\UserInterface;

/**
 * Implements hook_user_login().
 *
 * @param UserInterface $account
 * @throws \Drupal\Core\Entity\EntityMalformedException
 */
function observer_user_login(UserInterface $account) {

  // #############  用户登陆成功根据用户的专业给用户推送产品. ########## //
  switch ($account->get('field_profession')->value) {
    case 'A':
      //一大堆逻辑
      break;
    case 'B':
      // 一大堆逻辑
      break;
    case 'C':
      // 一大堆逻辑
      break;
    default:
      // 一大堆逻辑
      break;
  }

  // #############  用户登陆成功给用户发送一封邮件. ########## //
  // 发送邮件逻辑

  // #############  用户登陆成功判断后一次登陆时间如果超过1个月告诉通知营销部门. ########## //
  if (time() - $account->get('access')->value > 3000) {
     // 通知营销部门逻辑
  }

  // #############  用户登陆成功如果角色是超级管理员自动跳转到 /admin/contents 页面. ########## //
  if ($account->hasRole('administrator')) {
    // 跳转逻辑
  }

  // #############  用户登陆成功如果角色是网站管理员自动跳转到 /manage页面. ########## //
  if ($account->hasRole('webmaster')) {
    // 跳转逻辑
  }
}

这里的代码越后面会越乱,如果到时再增加一个需求登陆成功要做某事。可能又是一大堆逻辑.

现在改用普通的PHP观察者模式分离代码

PHP观察者模式

定义: src/Observer.php

<?php

namespace Drupal\observer;

use Drupal\user\UserInterface;

/**
 * 创建主题用来注册所有的观察者.
 *
 * Class Observer
 */
class Observer implements \SplSubject {

  private array $observers = [];

  private UserInterface $account;

  public function setAccount(UserInterface $account) {
    $this->account = $account;
  }

  public function getAccount() {
    return $this->account;
  }

  /**
   * 注册观察者.
   *
   * @param \SplObserver $observer
   */
  public function attach(\SplObserver $observer):void {
    $this->observers[] = $observer;
  }

  /**
   * 移掉观察者.
   *
   * @param \SplObserver $observer
   */
  public function detach(\SplObserver $observer):void {
    if ($key = array_search($observer,$this->observers, true)){
      unset($this->observers[$key]);
    }
  }

  /**
   * 通知观察者.
   */
  public function notify():void {
    foreach ($this->observers as $value) {
      $value->update($this);
    }
  }
}

 

分别定义功能代码:
src/Observers/Notice.php
src/Observers/NoticeMarkting.php
src/Observers/PushProduct.php
src/Observers/RedAdmin.php
src/Observers/RedWebmaster.php
代码都类似如下。只是类名不一样.

 
 
 
<?php
namespace Drupal\observer\Observers;

use Drupal\observer\Observer;
use SplSubject;

class Notice implements \SplObserver {

  public function update(Observer|SplSubject $subject):void {
    $account = $subject->getAccount();
    \Drupal::logger('observer')->notice('用户登陆成功给用户发送一封邮件: ' . $account->label());
  }
}

 

然后修改hook

<?php

use Drupal\observer\Observers\Notice;
use Drupal\observer\Observers\NoticeMarkting;
use Drupal\observer\Observers\PushProduct;
use Drupal\observer\Observers\RedAdmin;
use Drupal\observer\Observers\RedWebmaster;

use Drupal\user\UserInterface;
use Drupal\observer\Observer;

/**
 * Implements hook_user_login().
 *
 * @param UserInterface $account
 */
function observer_user_login(UserInterface $account) {
  $observer = new Observer();
  $observer->setAccount($account);

  $observer->attach(new Notice());
  $observer->attach(new NoticeMarkting());
  $observer->attach(new PushProduct());
  $observer->attach(new RedAdmin());
  $observer->attach(new RedWebmaster());

  $observer->notify();
}

 
Drupal的event和hook就很类似观察者模式. 现在可以使用event来实现这段代码
 


Event实现

这里定义一个新的模块: obevent

先自定义一个用户登陆的事件: src/Event/UserLoginEvent.php

<?php
namespace Drupal\obevent\Event;

use Drupal\Component\EventDispatcher\Event;
use Drupal\user\UserInterface;

class UserLoginEvent extends Event {

  const EVENT_NAME = 'custom_event_user_login';

  /**
   * @var \Drupal\user\UserInterface
   */
  public UserInterface $account;

  public function getAccount() {
    return $this->account;
  }

  public function setAccount($account) {
    $this->account = $account;
  }

  public function __construct(UserInterface $account) {
    $this->setAccount($account);
  }
}

 

定义功能实现的服务: obevent.services.yml

services:
  obevent.user_login_notice_event_subscriber:
    class: Drupal\obevent\EventSubscriber\LoginNoticeSubscriber
    tags:
      - { name: event_subscriber }
  obevent.user_login_notice_markting_event_subscriber:
    class: Drupal\obevent\EventSubscriber\LoginNoticeMarktingSubscriber
    tags:
      - { name: event_subscriber }
  obevent.user_login_push_product_event_subscriber:
    class: Drupal\obevent\EventSubscriber\LoginPushProductSubscriber
    tags:
      - { name: event_subscriber }

  obevent.user_login_red_admin_subscriber:
    class: Drupal\obevent\EventSubscriber\LoginRedAdminSubscriber
    tags:
      - { name: event_subscriber }

  obevent.user_login_red_webmater_subscriber:
    class: Drupal\obevent\EventSubscriber\LoginRedWebmasterSubscriber
    tags:
      - { name: event_subscriber }
 

定义功能实现代码:

src/EventSubscriber/LoginNoticeMarktingSubscriber.php
src/EventSubscriber/LoginNoticeSubscriber.php
src/EventSubscriber/LoginPushProductSubscriber.php
src/EventSubscriber/LoginRedAdminSubscriber.php
src/EventSubscriber/LoginRedWebmasterSubscriber.php

功能类似下面这样. 只是类名不一样

<?php

declare(strict_types=1);

namespace Drupal\obevent\EventSubscriber;

use Drupal\obevent\Event\UserLoginEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class LoginNoticeSubscriber implements EventSubscriberInterface {

  /**
   * Kernel request event handler.
   */
  public function onUserLogin(UserLoginEvent $event): void {
    $account = $event->getAccount();
    \Drupal::logger('observer')->notice('用户登陆成功给用户发送一封邮件: ' . $account->label());
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      UserLoginEvent::EVENT_NAME => ['onUserLogin'],
    ];
  }

}
 
 
修改obevent.module文件.
<?php

use Drupal\obevent\Event\UserLoginEvent;
use Drupal\user\UserInterface;

/**
 * Implements hook_user_login().
 *
 * @param UserInterface $account
 */
function obevent_user_login(UserInterface $account) {
  $event = new UserLoginEvent($account);
  \Drupal::service('event_dispatcher')->dispatch($event, UserLoginEvent::EVENT_NAME);
}
标签: PHP 设计模式