4、觀察者模式(Observer):
又叫發布訂閱模式,當一個主體對象發生改變時,依賴它的多個觀察者對像都得到通知並自動更新響應。就像報社一樣,今天發布的消息只要是看這份報紙的人看到的都是同樣的內容。如果發布另一份報紙,也是一樣的。
好處:廣播式通信,範圍大,並以一呼百應,以操作一個組團,「公有製」。
弊端:無法單獨操作組團裡的個體,且無法實施依需求分配。
應用場景:操作多個對象,並操作相同。
程式碼實作:
[php] view plaincopy
<?php /** * 优才网公开课示例代码 * * 观察者模式 Observer * * @author 优才网全栈工程师教研组 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后 //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立 class Order{ //订单号 private $id = ''; //用户ID private $userId = ''; //用户名 private $userName = ''; //价格 private $price = ''; //下单时间 private $orderTime = ''; //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理 public function __set($name, $value){ if (isset($this->$name)){ $this->$name = $value; } } //获取订单属性 public function __get($name){ if (isset($this->$name)){ return $this->$name; } return ""; } } //假设的DB类,便于测试,实际会存入真实数据库 class FakeDB{ public function save($data){ return true; } } class Client { public static function test() { //初始化一个订单数据 $order = new Order(); $order->id = 1001; $order->userId = 9527; $order->userName = "God"; $order->price = 20.0; $order->orderTime = time(); //向数据库保存订单 $db = new FakeDB(); $result = $db->save($order); if ($result){ //实际应用可能会写到日志文件中,这里直接输出 output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" ); //实际应用会调用邮件发送服务如sendmail,这里直接输出 output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" ); //实际应用会调用邮件发送服务如sendmail,这里直接输出 output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" ); } } } Client::test();
) 以中介對象封裝一系列的對象交互,中介使各物件不需要明確地相互引用。類似郵局,郵寄者和收件者不用自己跑很遠路,通過郵局就可以。 好處:簡化了物件之間的關係,減少子類別的產生。 弊端:中介對象可能變得非常複雜,系統難以維持。 應用場景:不需要顯示地建立互動。 程式碼實作: [php] view plaincopy
<?php /** * 优才网公开课示例代码 * * 观察者模式 Observer * * @author 优才网全栈工程师教研组 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后 //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立 class Order{ //订单号 private $id = ''; //用户ID private $userId = ''; //用户名 private $userName = ''; //价格 private $price = ''; //下单时间 private $orderTime = ''; //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理 public function __set($name, $value){ if (isset($this->$name)){ $this->$name = $value; } } //获取订单属性 public function __get($name){ if (isset($this->$name)){ return $this->$name; } return ""; } } //被观察者, 负责维护观察者并在变化发生是通知观察者 class OrderSubject implements SplSubject { private $observers; private $order; public function __construct(Order $order) { $this->observers = new SplObjectStorage(); $this->order = $order; } //增加一个观察者 public function attach(SplObserver $observer) { $this->observers->attach($observer); } //移除一个观察者 public function detach(SplObserver $observer) { $this->observers->detach($observer); } //通知所有观察者 public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } } //返回主体对象的具体实现,供观察者调用 public function getOrder() { return $this->order; } } //记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略 class ActionLogObserver implements SplObserver{ public function update(SplSubject $subject) { $order = $subject->getOrder(); //实际应用可能会写到日志文件中,这里直接输出 output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" ); } } //给用户发送订单确认邮件 (UserMailObserver) class UserMailObserver implements SplObserver{ public function update(SplSubject $subject) { $order = $subject->getOrder(); //实际应用会调用邮件发送服务如sendmail,这里直接输出 output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" ); } } //给管理人员发订单处理通知邮件 (AdminMailObserver) class AdminMailObserver implements SplObserver{ public function update(SplSubject $subject) { $order = $subject->getOrder(); //实际应用会调用邮件发送服务如sendmail,这里直接输出 output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" ); } } //假设的DB类,便于测试,实际会存入真实数据库 class FakeDB{ public function save($data){ return true; } } class Client { public static function test() { //初始化一个订单数据 $order = new Order(); $order->id = 1001; $order->userId = 9527; $order->userName = "God"; $order->price = 20.0; $order->orderTime = time(); //绑定观察者 $subject = new OrderSubject($order); $actionLogObserver = new ActionLogObserver(); $userMailObserver = new UserMailObserver(); $adminMailObserver = new AdminMailObserver(); $subject->attach($actionLogObserver); $subject->attach($userMailObserver); $subject->attach($adminMailObserver); //向数据库保存订单 $db = new FakeDB(); $result = $db->save($order); if ($result){ //通知观察者 $subject->notify(); } } } Client::test();
就像女朋友一樣,高興了牽你的手,不高興了遛狗。在兩種狀態下變現出不同的行為。
好處:避免if語句實用,方便增加新狀態,封裝了狀態轉換規則。
弊端:增加系統類別與物件的數量。
應用場景:用於物件的不同功能的轉換。
程式碼實作:
[php] view plaincopy
<?php /** * 优才网公开课示例代码 * * 中介者模式 Mediator * * @author 优才网全栈工程师教研组 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } abstract class Mediator { // 中介者角色 abstract public function send($message,$colleague); } abstract class Colleague { // 抽象对象 private $_mediator = null; public function __construct($mediator) { $this->_mediator = $mediator; } public function send($message) { $this->_mediator->send($message,$this); } abstract public function notify($message); } class ConcreteMediator extends Mediator { // 具体中介者角色 private $_colleague1 = null; private $_colleague2 = null; public function send($message,$colleague) { if($colleague == $this->_colleague1) { $this->_colleague1->notify($message); } else { $this->_colleague2->notify($message); } } public function set($colleague1,$colleague2) { $this->_colleague1 = $colleague1; $this->_colleague2 = $colleague2; } } class Colleague1 extends Colleague { // 具体对象角色 public function notify($message) { output(sprintf('Colleague-1: %s', $message)); } } class Colleague2 extends Colleague { // 具体对象角色 public function notify($message) { output(sprintf('Colleague-2: %s', $message)); } } class Client { public static function test(){ // client $objMediator = new ConcreteMediator(); $objC1 = new Colleague1($objMediator); $objC2 = new Colleague2($objMediator); $objMediator->set($objC1,$objC2); $objC1->send("to c2 from c1"); $objC2->send("to c1 from c2"); } } Client::test();
<?php /** * 优才网公开课示例代码 * * 状态模式 State * * @author 优才网全栈工程师教研组 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } abstract class ILift { //电梯的四个状态 const OPENING_STATE = 1; //门敞状态 const CLOSING_STATE = 2; //门闭状态 const RUNNING_STATE = 3; //运行状态 const STOPPING_STATE = 4; //停止状态; //设置电梯的状态 public abstract function setState($state); //首先电梯门开启动作 public abstract function open(); //电梯门有开启,那当然也就有关闭了 public abstract function close(); //电梯要能上能下,跑起来 public abstract function run(); //电梯还要能停下来 public abstract function stop(); } /** * 电梯的实现类 */ class Lift extends ILift { private $state; public function setState($state) { $this->state = $state; } //电梯门关闭 public function close() { //电梯在什么状态下才能关闭 switch ($this->state) { case ILift::OPENING_STATE: //如果是则可以关门,同时修改电梯状态 $this->setState(ILift::CLOSING_STATE); break; case ILift::CLOSING_STATE: //如果电梯就是关门状态,则什么都不做 //do nothing; return ; break; case ILift::RUNNING_STATE: //如果是正在运行,门本来就是关闭的,也说明都不做 //do nothing; return ; break; case ILift::STOPPING_STATE: //如果是停止状态,本也是关闭的,什么也不做 //do nothing; return ; break; } output('Lift colse'); } //电梯门开启 public function open() { //电梯在什么状态才能开启 switch($this->state){ case ILift::OPENING_STATE: //如果已经在门敞状态,则什么都不做 //do nothing; return ; break; case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以开启 $this->setState(ILift::OPENING_STATE); break; case ILift::RUNNING_STATE: //正在运行状态,则不能开门,什么都不做 //do nothing; return ; break; case ILift::STOPPING_STATE: //停止状态,淡然要开门了 $this->setState(ILift::OPENING_STATE); break; } output('Lift open'); } ///电梯开始跑起来 public function run() { switch($this->state){ case ILift::OPENING_STATE: //如果已经在门敞状态,则不你能运行,什么都不做 //do nothing; return ; break; case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以运行 $this->setState(ILift::RUNNING_STATE); break; case ILift::RUNNING_STATE: //正在运行状态,则什么都不做 //do nothing; return ; break; case ILift::STOPPING_STATE: //停止状态,可以运行 $this->setState(ILift::RUNNING_STATE); } output('Lift run'); } //电梯停止 public function stop() { switch($this->state){ case ILift::OPENING_STATE: //如果已经在门敞状态,那肯定要先停下来的,什么都不做 //do nothing; return ; break; case ILift::CLOSING_STATE: //如是电梯时关闭状态,则当然可以停止了 $this->setState(ILift::CLOSING_STATE); break; case ILift::RUNNING_STATE: //正在运行状态,有运行当然那也就有停止了 $this->setState(ILift::CLOSING_STATE); break; case ILift::STOPPING_STATE: //停止状态,什么都不做 //do nothing; return ; break; } output('Lift stop'); } } class Client { public static function test() { $lift = new Lift(); //电梯的初始条件应该是停止状态 $lift->setState(ILift::STOPPING_STATE); //首先是电梯门开启,人进去 $lift->open(); //然后电梯门关闭 $lift->close(); //再然后,电梯跑起来,向上或者向下 $lift->run(); //最后到达目的地,电梯挺下来 $lift->stop(); } } Client::test();