PHP中多种常用的设计模式举例
1.适配器模式,可以将截然不同的函数接口封装成统一的API
2.实际应用举例,PHP的数据库操作有mysql,musqli,pdo三种,可以用适配器模式统一成一致。类似的场景还有cache适配器,将memcache,redis,file,apc等不同的缓存函数,统一成一致
Database.php
<?php interdace IDatabase{ function connect($host, $user, $passwd, $dbname); function query($sql); function close(); }
在Database.php中定义了数据库操作的接口,接下来将MySQL、MySQLi、PDO三种数据库操作适配为接口中定义的方法。
MySQL.php
<?php namespace Database; class MySQL implements IDatabase{ protected $conn; function connect($host, $user, $passwd, $dbname){ $conn = mysql_connect($host, $user, $passwd); mysql_select_db($dbname, $conn); this->conn = $conn; } function query($sql){ $res = mysql_query($sql, $this->conn); return $res; } function close(){ mysql_close($this->conn); } }
MySQLi.php
<?php namespace Database; class MySQLi implements IDatabase{ protected $conn; function connect($host, $user, $passwd, $dbname){ $conn = mysqli_connect($host, $user, $passwd, $dbname); this->conn = $conn; } function query($sql){ $res = mysqli_query($this->conn, $sql); return $res; } function close(){ mysqli_close($this->conn); }
PDO.php
<?php namespace Database; class PDO implements IDatabase{ protected $conn; function connect($host, $user, $passwd, $dbname){ new \PDO("mysql:host=$host;dbname=$dbname", $user, $passwd); $this->conn = $conn; } function query($sql){ return $this->conn->query($sql); } function close(){ unset($this->conn); }
然后在应用中可以这样使用:
index.php
<?php $db = new Database\MySQL(); $db->connect('localhost', 'root', 'root', 'test'); $db->query('show database'); $db->close();
或者实例化其他类型的数据库,只需要使用同一种标准。
1.策略模式,将一组特定行为和算法封装成类,以适应某些特定的上下文环境,这种模式就是策略模式
2.实际应用举例,假如一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有广告位展示不同的广告
3.使用策略模式可以实现Ioc,依赖倒置、控制反转
UserStrategy.php
<?php interface UserStrategy { function showAd(); function showCategory(); }
UserStrategy.php文件定义了用户策略的接口,面对不同的用户将分别实现相应的内容。
FemaleUserStrategy.php
<?php class FemaleUserStrategy implements UserStrategy { function showAd() { echo "2017新款女装"; } function showCategory() { echo "女装"; } }
MaleUserStrategy.php
<?php class MaleUserStrategy implements UserStrategy { function showAd() { echo "IPhone7"; } function showCategory() { echo "电子产品"; } }
在index.php中这样做就可以实现从硬编码到实现解耦,即不需要修改Page类的内容,只需要传递不同的参数。
index.php
<?php class Page { function index() { echo "AD:"; $this->strategy->showAd(); echo "Category:"; $this->strategy->showCategory(); } function setStrategy(UserStrategy $strategy) { $this->strategy = $strategy; } } $page = new Page(); if (isset($_GET['female'])) { $strategy = new FemaleUserStrategy(); } else { $strategy = new MaleUserStrategy(); } $page->setStrategy($strategy); $page->index();
数据对象映射模式,是将对象和数据存储映射起来,对一个对象的操作会映射为对数据存储的操作。
在下面的代码中实现数据对象映射模式,我们将实现一个ORM类,将复杂的SQL语句映射成对象属性的操作。
User.php
<?php class User { public $id; public $mobile; public $regtime; protected $db; function __construct($id) { // 数据库查询操作 $this->db = new MySQLi(); $this->db->connect('localhost', 'root', 'root', 'test1'); $res = $this->db->query("select * from user where id={$this->id} limit 1"); $data = $res->fetch_assoc(); $this->id = $data['id']; $this->name = $data['name']; $this->mobile = $data['imobile]; $this->regtime = $data[regtimed']; } function __destruct() { // 数据库写入操作 $this->db->query("update user set name = '{$this->name}', mobile='{$this->mobile}', regtime='{$this->regtime}' where id = {$this->id} limit 1"); } }
这样来使用:
index.php
<?php $user = new User(1); $user->mobile = '18812345678'; $user->name = 'test'; $user->regtime = date('Y-m-d H:i:s');
下面结合使用数据对象映射模式,工厂模式,注册器模式:
index.php
<?php class Page { function index() { $user = Factory::getUser(1); // 工厂模式的实现 $user->name = 'rango'; $this->test(); } function test() { $user = Factory::getUser(1); // 工厂模式的实现 $user->mobile = '18844448888'; } } $page = new Page();
Factory.php
<?php class Factory { static function getUser($id) { // 注册器模式的实现 $key = 'user_'.$id; $user = Register::get($key); if (!user) { $user = new User($id); Register::set($key,$user); } return $user; } }
1.观察者模式(Observer),当一个对象状态发生改变时,依赖它的对象全部会收到通知,并自动更新
2.场景:一个事件发生后,要执行一连串更新操作,传统的编程方式,就是在事件的代码之后直接加入处理逻辑。当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件主体的代码
3.观察者模式实现了低耦合,非侵入式的通知与更新机制
index.php
<?php class Event extends EventGenerator { function trigger() { echo "Event<br />"; $this->notify(); } } class Observer1 implements Obsrver { function update($event_info = null) { echo "逻辑1<br />"; } } class Observer2 implements Obsrver { function update($event_info = null) { echo "逻辑2<br />"; } } $event = new Event; $event->addObserver(new Observer1); // 观察者模式的实现 $event->addObserver(new Observer2); // 观察者模式的实现 $event->trigger();
EventGenerator.php
<?php abstract class EventGenerator { private $observers = array(); function addObserver(Observer $observer) { $this->observers[] = $observer; } function notify() { foreach($this->observers as $ovserver) { $observer->update(); } } }
Observer.php
<?php interface Observer { function update($event_info = null); }
1.与工厂模式作用类似,都是用来创建对象
2.与工厂模式的实现不同,原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。这样就免去了类创建时重复的初始化操作
3.原型模式适用于大对象的创建。创建一个大对象需要很大的开销,如果每次new就会消耗很大,原型模式内存拷贝即可
Canvas.php
<?php class Canvas { ... }
index.php将实例化Canvas类以实现图像绘制,并使用关键字clone实现原型模式:
index.php
<?php $prototype = new Canvas(); $prototype->init(); // 原型模式的实现 $canvas1 = clone $prototype; $canvas2 = clone $prototype; $canvas1->rect(1,2,3,4); $canvas1->draw(); $canvas2->rect(5,6,7,8); $canvas2->draw();
1.装饰器模式(Decorator),可以动态地添加修改类的功能
2.一个类提供了一项功能,如果要在修改并添加额外的功能,传统的编程模式,需要写一个子类继承它,并重新实现类的方法
3.使用装饰器模式,仅需要在运行时添加一个装饰器对象即可实现,可以实现最大的灵活性
DrawDecorator.php
<?php interface DrawDecorator { function beforeDraw(); function afterDraw(); }
DrawDecorator.php定义了装饰器的接口,将会在装饰器类中用到。
Canvas.php
<?php class Canvas { protected $decorator = array(); function draw() { // 装饰器模式实现点 $this->beforeDraw(); ... $this->afterDraw(); } function addDecorator(DrawDecorator $decorator) { $this->decorators[] = $decorator; } function beforeDraw() { foreach($this->decorators as $decorator) { $decorator->beforeDraw(); } } fucntion afterDraw() { $decorators = array_reverse($this->decorators); // 反转,后进先出 foreach($decorators as $decorator) { $decorator->afterDraw(); } } }
ColorDrawDecorator.php
<?php // 第一个装饰器:颜色装饰器 class ColorDrawDecortor implements DrawDecorator { function beforeDraw() { ... } function afterDraw() { ... } }
1.迭代器模式,在不需要了解内部实现的前提下,遍历一个聚合对象的内部元素
2.相比于传统的编程模式,迭代器模式可以隐藏遍历元素所需的操作
index.php
<?php $users = new AllUser(); foreach($users as $user) { var_dump($user); }
迭代器的实现:
AllUser.php
<?php $canvas1 = new Canvas(); $canvas1->init(); $canvas1->addDecorator(new ColorDrawDecortor()); // 添加装饰器 $canvas1->draw();
这样来使用:
index.php
<?php class AllUser implements \Iterator { protected $ids; protected $data = array(); protected $index; function __construct() { $db = Factory::getDatabase(); $result = $db->query('select id from user'); $this->ids = $result->fetch_all(MUSQLI_ASSOC); } function current() { $id = $this->ids[$this->index]['id']; return Factory::getUser($id); } function next() { $this->index ++; } function valid() { return $this->id < count($this->ids); } function rewind() { $this->index = 0; } function key() { return $this->index; } }
1.在客户端与实体之间建立一个代理对象(proxy),客户端对实体进行操作全部委派给代理对象,隐藏实体的具体实现细节
2.Proxy还可以与业务代码分离,部署到另外的服务器,业务代码中通过RPC来委派任务
index.php
$proxy = new Proxy(); $proxy->getUserName(); $proxy->setUserName($id, $proxy);
Proxy.php
<?php class Procy implemets IUserProxy { function getUserName($id) { $db = Factory::getDatabase('slave'); $db->query('select name from user where id = 1 limit 1'); } function setUserName($id, $name) { $db1 = Factory::getDatabase('master'); $db->query('update user name = $name from user where id = 1 limit 1'); } }
IUserProxy.php
<?php interface IUserName { function getUserName($id); function setUserName($id, $name); }