相关内容:
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 就会消耗很大,原型模式仅需要内存拷贝就可以