PHP 设计模式之策略模式、数据对象映射模式、观察者模式、原型模式、装饰器模式、迭代器模式、代理模式

mac2025-07-17  5

相关内容: 1. PHP 三种基础设计模式(工厂模式、单例模式、注册器模式)以及适配器模式 2. PHP 设计模式之策略模式、数据对象映射模式、观察者模式、原型模式、装饰器模式、迭代器模式、代理模式

本篇概要:

1. 策略模式;2. 数据对象映射模式;2.1 基础;2.2 复杂案例; 3. 观察者模式;4. 原型模式;5. 装饰器模式;6. 迭代器模式;7. 代理模式。

1. 策略模式;

策略模式,是将一组特定的行为和算法封装成类,以适应某些特定的上下文环境

实际应用举例,假设一个电商网站,针对男女性用户要各自跳转到不同的商品类目,并且所有的广告位展示不同的广告。在传统编程中,会在代码中加入 if…else… 的判断,这属于硬编码形式,假设要增加一种新类型判断,那所有的 if…else… 都要进行修改。如果使用了策略模式,它与上下文的管理是中立的,互相不依赖。以上如果需要增加一个新类型,就只需要增加一种新策略即可。

实例:首先声明一个策略的接口文件,会约定策略会有哪些行为

新增:Library/UserStrategy.php

<?php namespace Library; interface UserStrategy{ // 展示广告; function showAd(); // 展示类目; function showCategory(); } 定义策略的具体实现新增男性策略:Library/MaleUserStrategy.php <?php namespace Library; // 男性策略 class MaleUserStrategy implements UserStrategy{ function showAd(){ echo 'man-ad'; } function showCategory(){ echo 'man-category'; } } 新增女性策略:Library/FemaleUserStrategy.php <?php namespace Library; // 女性策略 class FemaleUserStrategy implements UserStrategy{ function showAd(){ echo 'woman-ad'; } function showCategory(){ echo 'woman-category'; } } 调用:index.php <?php class Page{ protected $strategy; function index(){ // index 里不需要判断,只需要调用策略对象的方法 $this->strategy->showAd(); echo "<br>"; $this->strategy->showCategory(); } function setStrategy(\Library\UserStrategy $strategy){ $this->strategy = $strategy; } } $page = new Page(); // 传入策略对象(根据具体业务) // 实现了从硬编码到解耦 if(isset($_GET['male'])){ $strategy = new \Library\MaleUserStrategy(); }else{ $strategy = new \Library\FemaleUserStrategy(); } $page->setStrategy($strategy); $page->index(); 使用策略模式可以实现 Ioc,依赖倒置、控制反转在写上面的 page() 类的时候,并不需要去定义所依赖的类,在最终执行的时候,才把关系做了一个绑定。也可以很方便的去替换掉某一个类

2. 数据对象映射模式;

2.1 基础;

数据对象映射模式,是将对象和数据储存映射起来,对一个对象的操作会映射为对数据储存的操作比如在代码中 new 一个对象,使用数据对象映射模式就可以将对象的一些操作,比如设置的一些属性,就会自动保存到数据库,和数据库中表的一条记录对应起来在代码中实现数据对象映射模式,实现一个 ORM 类、将复杂的 SQL 语句映射成对象属性的操作,可以有效的屏蔽一些数据库底层的调用实例:创建数据表 CREATE TABLE `t1`.`user` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(32) NOT NULL , `mobile` VARCHAR(11) NOT NULL , `regtime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP , PRIMARY KEY (`id`)) ENGINE = InnoDB; // 插入一条数据 INSERT INTO user SET name='Tom', mobile='13333333333'; 新增:Library/User.php <?php namespace Library; class User{ public $id; public $name; public $mobile; public $regtime; protected $db; function __construct(){ $this->db = new \Library\Database\MySQLi(); $this->db->connect('127.0.0.1', 'root', 'asdf', 't1'); $res = $this->db->query("SELECT * FROM user LIMIT 1"); $data = $res->fetch_assoc(); $this->id = $data['id']; $this->name = $data['name']; $this->mobile = $data['mobile']; $this->regtime = $data['regtime']; } function __destruct(){ $this->db->query("UPDATE user SET name='{$this->name}', mobile='{$this->mobile}', regtime='{$this->regtime}' WHERE id = 1"); } } 修改 index.php <?php $user = new Library\User(1); // 读取数据 // var_dump($user->id, $user->mobile, $user->name, $user->regtime); // exit; // 修改数据 $user->mobile = '14333333333'; $user->name = 'Tom2'; $user->regtime = date('Y-m-d H:i:s');

2.2 复杂案例;

结合使用数据对象映射模式、工厂模式、注册器模式不使用工厂模式:修改 index.php <?php // 模拟场景:在 index() 和 test() 里分别对 User() 对象的两个字段进行修改 // 不使用工厂模式的思路: class Page{ function index(){ $user = new Library\User(1); $user->name = 'Jerry'; $this->test(); } function test(){ $user = new Library\User(1); $user->mobile = '18333333333'; } } // User() 类里的析构方法会调用两次 $page = new Page(); $page->index(); 优化:修改 Library/Factory.php <?php namespace Library; // 工厂类 class Factory{ static function createDatabase(){ // $db = new Database; $db = Database::getInstance(); // 单例 Register::set('db1',$db); // 注册 return $db; } // 新增方法: static function getUser($id){ // 使用注册器模式,将这个对象在任何地方都用同一个对象,解决对象重复 new 的问题 // $id 不同表示不同的对象,同一个 $id 就只要 new 一次对象 $key = 'user_' . $id; $user = Register::get($key); if(!$user){ $user = new User($id); // 对象保存在注册器中 Register::set($key, $user); } return $user; } } 修改 index.php <?php // 模拟场景:在 index() 和 test() 里分别对 User() 对象的两个字段进行修改 class Page{ function index(){ // 工厂模式生成对象 $user = Library\Factory::getUser(1); var_dump($user); $user->name = 'Jerry'; $this->test(); } function test(){ $user = Library\Factory::getUser(1); var_dump($user); $user->mobile = '18333333333'; } } // User() 类里的析构方法只调用了一次 $page = new Page(); $page->index();

3. 观察者模式;

观察者模式(Observer),当一个对象状态发生改变时,依赖它的对象全部会收到通知,并自动更新场景:一个事件发生后,要执行一连串更新操作。传统的编程方式就是在事件的代码之后直接加入处理逻辑。当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件主体的代码观察者模式实现了低耦合,非侵入式的通知和更新机制实例:创建 Library/Observer.php 观察者接口 <?php namespace Library; interface Observer{ // 事件发生后的更新操作 function update($event_info = null); } 创建 Library/EventGenerator.php <?php namespace Library; abstract class EventGenerator{ // 增加的观察者保存到数组 // 观察者对于事件发生者不可见 // 它并不知道有哪些人关注了这个事件,只知道事件发生了 private $observers = []; // 增加观察者 function addObserver(Observer $observer){ $this->observers[] = $observer; } // 通知事件发生 // 逐个去通知所有的观察者 function notify(){ foreach ($this->observers as $observer){ // 逐个去调用每个观察者的 update(),让观察者执行自动更新 $observer->update(); } } } 更新 index.php class Event extends \Library\EventGenerator{ // 触发新事件 function trigger(){ echo "Event on<br>"; // 通知所有观察者执行更新 $this->notify(); // 传统编程逻辑就是在事件代码之后开始写更新逻辑 // 逻辑越来越多,代码越来越多,耦合在一起,是侵入式的,变得难以维护 // 但实际 function 1-3 分别是不同的业务模块 // echo "function 1<br>"; // echo "function 2<br>"; // echo "function 3<br>"; } } // 观察者 1 class Observer1 implements \Library\Observer{ function update($event_info = null){ echo "function 1<br>"; } } $event = new Event(); // 事件发生后,observer 的 update() 都会触发 // 代码没有任何一个地方耦合的直接在事件主体写逻辑 // 也可以动态关闭观察者的逻辑 $event->addObserver(new Observer1); $event->trigger();

4. 原型模式;

与工厂模式作用类似,都是用来创建对象与工厂模式实现不同,原型模式是先创建好一个原型对象,然后通过 clone 原型对象来创建新的对象。这样就免去了类创建时重复的初始化动作原型模式适用于大对象的创建。创建一个大对象需要很大的开销,如果每次 new 就会消耗很大,原型模式仅需要内存拷贝就可以

5. 装饰器模式;

6. 迭代器模式;

7. 代理模式。

最新回复(0)