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

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

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 复杂案例;

<?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 就会消耗很大,原型模式仅需要内存拷贝就可以
发布了119 篇原创文章 · 获赞 12 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/hualaoshuan/article/details/102857856