Php设计模式(三):行为型模式part1
原文详见:http://www.ucai.cn/blogdetail/7023?mid=1&f=5
可以在线运行查看效果哦!
在上一篇我们讲了结构型模式,结构型模式是讨论类和对象的结构的。总共有7种。而今天我们来介绍一下行为型模式。
一、什么是行为型模式?
行为型模式:
就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。
二、行为型模式的种类
大体上分为三个大类:常见模式、已知模式、深度模式
常见模式包括: 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态式、职责链模式、策略模式
已知模式包括:备忘录模式
深度模式包括:解释器模式 访问者模式
下面来介绍常见模式
- 常见模式
1、模版方法模式(Template):
定义一个操作中的算法骨架,而将一些实现步骤延迟到子类当中 实现。就像一个豆浆机,不管放进去的是红豆还是黑豆,出来的都是豆浆。
好处:扩展性好,封装不变的代码,扩展可变的代码。
弊端:灵活性差,不能改变骨架部分。
应用场景:一类或一组具有共性的事物中
代码实现
<?php /**
* 优才网公开课示例代码
*
* 模板方法模式 Template
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/ function output($string) {
echo $string . "\n";
} class Request { public $token = ''; public function __construct() {
$this->token = '0c6b7289f5334ed2b697dd461eaf9812';
} } class Response { public function render($content) {
output(sprintf('response-render: %s', $content));
} public function redirect($uri) {
output(sprintf('response-redirect: %s', $uri));
} public function json($data) {
output(sprintf('response-data: %s', json_encode($data)));
} } //父类,抽象类
abstract class Controller{
//封装了输入输出
protected $request;
protected $response; //返回数据
protected $data = 'data'; public function __construct($request, $response){
$this->request = $request;
$this->response = $response;
} //执行请求函数,定义总体算法(template method),final防止被复写(不允许子类改变总体算法)
public final function execute(){
$this->before();
if ($this->valid()){
$this->handleRequest();
}
$this->after();
} //定义hook method before,做一些具体请求的前置处理
//非abstract方法,子类可以选择覆盖或不覆盖,默认什么都不做
protected function before(){ } //定义hook method valid,做请求的数据验证
//非abstract方法,子类可以选择覆盖或不覆盖,默认返回验证通过
protected function valid(){
return true;
} //定义hook method handleRequest,处理请求
//定义为abstract方法,子类必须实现或也声明为抽象方法(由子类的子类负责实现)
abstract function handleRequest(); //定义hook method after,做一些请求的后置处理
//非abstract方法,子类可以选择覆盖或不覆盖,默认直接输出数据
protected function after(){
$this->response->render($this->data);
}
} //子类1,实现父类开放的具体算法
class User extends Controller{
//覆盖before方法,实现具体算法,这是一个处理用户数据操作的控制器
//因此,我们选择在before里面判断用户是否已经登录了,这里简单判断下session数据
function before(){
if (empty($_SESSION['auth'])){
//没登录就直接跳转了,不再执行后续的操作
$this->response->redirect("user/login.php");
}
} //覆盖valid方法,这里我们验证用户提交数据中有没有带验证token
function valid(){
if (isset($this->request->token)){
return true;
}
return false;
} //覆盖handleRequest方法,必选,以为父类中声明了abstract了
function handleRequest(){
//做具体处理,一般根据参数执行不同的业务逻辑
} //这个类我们选择不覆盖after方法,使用默认处理方式
} //子类2,实现父类开放的具体算法
class Post extends Controller{
//这个类我们选择不覆盖before方法,使用默认处理方式 //这个类我们选择不覆盖valid方法,使用默认处理方式 //覆盖handleRequest方法,必选,以为父类中声明了abstract了
function handleRequest(){
//做具体处理,一般根据参数执行不同的业务逻辑
$this->data = array('title' => 'ucai');
} //覆盖after方法,使用json格式输出数据
function after(){
$this->response->json($this->data);
}
} class Client { public static function test(){ $request = new Request();
$response = new Response(); //最终调用
$user = new User($request, $response);
$user->execute(); //最终调用
$post = new Post($request, $response);
$post->execute(); } } Client::test();
2、命令模式(Command) :
行为请求者与行为实现者解耦。就像军队里的“敬礼”,不管是谁听到这个命令都会做出标准的敬礼动作。
好处:便于添加和修改行为,便于聚合多个命令。
弊端:造成过多具体的命令类。
应用场景:对要操作的对象,进行的相同操作。
代码实现
<?php
/**
* 优才网公开课示例代码
*
* 命令模式 Command
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/ function output($string) {
echo $string . "\n";
} class Document { private $name = ''; public function __construct($name) {
$this->name = $name;
} public function showText() {
output(sprintf("showText: %s", $this->name));
} public function undo() {
output(sprintf("undo-showText: %s", $this->name));
} } class Graphics { private $name = ''; public function __construct($name) {
$this->name = $name;
} public function drawCircle() {
output(sprintf("drawCircle: %s", $this->name));
} public function undo() {
output(sprintf("undo-drawCircle: %s", $this->name));
} } class Client { public static function test() { $document = new Document('A');
$graphics = new Graphics('B'); $document->showText();
$graphics->drawCircle(); $document->undo(); } } Client::test(); <?php /**
* 优才网公开课示例代码
*
* 命令模式 Command
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/ function output($string) {
echo $string . "\n";
} interface Command {
public function execute();
public function undo();
} class Document implements Command { private $name = ''; public function __construct($name) {
$this->name = $name;
} public function execute() {
output(sprintf("showText: %s", $this->name));
} public function undo() {
output(sprintf("undo-showText: %s", $this->name));
} } class Graphics implements Command { private $name = ''; public function __construct($name) {
$this->name = $name;
} public function execute() {
output(sprintf("drawCircle: %s", $this->name));
} public function undo() {
output(sprintf("undo-drawCircle: %s", $this->name));
} } class Client { public static function test() { $array = array(); array_push($array, new Document('A'));
array_push($array, new Document('B'));
array_push($array, new Graphics('C'));
array_push($array, new Graphics('D')); foreach ($array as $command) {
$command->execute();
} $top = array_pop($array);
$top->undo(); } } Client::test(); <?php /**
* 优才网公开课示例代码
*
* 命令模式 Command
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/ function output($string) {
echo $string . "\n";
} interface Command {
public function execute();
public function undo();
} class Document { private $name = ''; public function __construct($name) {
$this->name = $name;
} public function showText() {
output(sprintf("showText: %s", $this->name));
} public function undo() {
output(sprintf("undo-showText: %s", $this->name));
} } class Graphics { private $name = ''; public function __construct($name) {
$this->name = $name;
} public function drawCircle() {
output(sprintf("drawCircle: %s", $this->name));
} public function undo() {
output(sprintf("undo-drawCircle: %s", $this->name));
} } class DocumentCommand implements Command { private $obj = ''; public function __construct(Document $document) {
$this->obj = $document;
} public function execute() {
$this->obj->showText();
} public function undo() {
$this->obj->undo();
} } class GraphicsCommand implements Command { private $obj = ''; public function __construct(Graphics $graphics) {
$this->obj = $graphics;
} public function execute() {
$this->obj->drawCircle();
} public function undo() {
$this->obj->undo();
} } class Client { public static function test() { $array = array(); array_push($array, new DocumentCommand(new Document('A')));
array_push($array, new DocumentCommand(new Document('B')));
array_push($array, new GraphicsCommand(new Graphics('C')));
array_push($array, new GraphicsCommand(new Graphics('D'))); foreach ($array as $command) {
$command->execute();
} $top = array_pop($array);
$top->undo(); } } Client::test();
3、迭代器模式 (Iterator):
访问聚合对象内容而不暴露内部结构。就像一个双色球彩票开奖一样,每次都是摇出七个球,不能能摇不是七个球的中奖号码组合。
好处:以不同方式遍历一个集合。
弊端:每次遍历都是整个集合,不能单独取出元素。
应用场景:需要操作集合里的全部元素。
代码实现
<?php /**
* 优才网公开课示例代码
*
* 迭代器模式 Iterator
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/ function output($string) {
echo $string . "\n";
} class RecordIterator implements Iterator{ private $position = 0; //注意:被迭代对象属性是私有的
private $records = array(); public function __construct(Array $records) {
$this->position = 0;
$this->records = $records;
} function rewind() {
$this->position = 0;
} function current() {
return $this->records[$this->position];
} function key() {
return $this->position;
} function next() {
++$this->position;
} function valid() {
return isset($this->records[$this->position]);
}
} class PostListPager { protected $record = array();
protected $total = 0;
protected $page = 0;
protected $size = 0; public function __construct($category, $page, $size) { $this->page = $page;
$this->size = $size; // query db $total = 28;
$this->total = $total; $record = array(
0 => array('id' => '1'),
1 => array('id' => '2'),
2 => array('id' => '3'),
3 => array('id' => '4'),
); //
$this->record = $record; } public function getIterator() {
return new RecordIterator($this->record);
} public function getMaxPage() {
$max = intval($this->total / $this->size);
return $max;
} public function getPrevPage() {
return max($this->page - 1, 1);
} public function getNextPage() {
return min($this->page + 1, $this->getMaxPage());
} } class Client { public static function test(){ $pager = new PostListPager(1, 2, 4); foreach ($pager->getIterator() as $key => $val) {
output(sprintf('Key[%d],Val[%s]', $key, json_encode($val)));
} output(sprintf('MaxPage[%d]', $pager->getMaxPage()));
output(sprintf('Prev[%d]', $pager->getPrevPage()));
output(sprintf('Next[%d]', $pager->getNextPage())); $iterator = $pager->getIterator();
while($iterator->valid()){
print_r($iterator->current());
$iterator->next();
}
$iterator->rewind(); } } Client::test();
4、观察者模式(Observer) :
又叫发布订阅模式,当一个主体对象发生改变时,依赖它的多个观察者对象 都得到通知并自动更新响应。就像报社一样,今天发布的消息只要是看这份报纸的人看到的都是同样的内容。如果发布另一份报纸,也是一样的。
好处:广播式通信,范围大,一呼百应,便于操作一个组团,“公有制”。
弊端:不能单独操作组团里的个体,不能实行按需分配。
应用场景:操作多个对象,并操作相同。
代码实现:
<?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 /**
* 优才网公开课示例代码
*
* 观察者模式 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();
《接下一篇》
Php设计模式(三):行为型模式part1的更多相关文章
- Java经典23种设计模式之行为型模式(三)
本文接着介绍11种行为型模式里的备忘录模式.观察者模式.状态模式. 一.备忘录模式 在不破坏封装性的前提下,捕获一个对象的内部状态.并在该对象之外保存这个状态.这样以后就能够将该对象恢复到原先保存的状 ...
- Java设计模式之创建型模式
创建型模式分为五类:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式 一.工厂方法模式:接口-实现类.工厂类
- java设计模式---三种工厂模式之间的区别
简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式.其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性. 本文是本人对这三种模式学习后的一个小结以及对他 ...
- Java设计模式之行为型模式
行为型模式共11种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 策略模式:策略模式的决定权在用户,系统本身提供不同 ...
- Java经典23结构模型的设计模式(三)------附加代理模式、适配器型号、Facade模式的差异
本文介绍了7样的结构模型中的其余2种:轻量级.代理模式. 一.享元模式FlyWeight 享元模式比較简单且重要,在非常多场合都被用到.仅仅只是封装起来了用户看不到.其概念:运用共享内存技术最大限度的 ...
- Java设计模式之职责型模式总结
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6548127.html 所谓职责型模式,就是采用各种模式来分配各个类的职责. 职责型模式包括 ...
- java设计模式三种工厂模式简单介绍
一.简单工厂模式 概述:简单工厂模式的创建意图就是,把对类的创建初始化全都交给一个工厂来执行,而用户不需要去关心创建的过程是什么样的,只用告诉工厂我想要什么就行了.而这种方法的缺点也很明显,违背了设计 ...
- Java经典23种设计模式之行为型模式(二)
本文接着介绍行为型模式里的解释器模式.迭代器模式.中介者模式. 一.解释器模式Interpret 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言的中的句子. 1 ...
- GoF的23种设计模式之行为型模式的特点和分类(2)
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配. 行为型模式分为类行为模式和对象行为模式,前者采用继 ...
随机推荐
- SoC嵌入式软件架构设计II:没有MMU的CPU虚拟内存管理的设计和实现方法
大多数的程序代码是必要的时,它可以被加载到内存中运行.手术后,可直接丢弃或覆盖其它代码. 我们PC然在同一时间大量的应用,地址空间差点儿能够整个线性地址空间(除了部分留给操作系统或者预留它用).能够觉 ...
- Android setDisplayOptions 具体的使用说明
Android有几个地方使用位计算.实例Intent Flags,它们的定义View onMeasure(int widthMeasureSpec, int heightMeasureSpec),并且 ...
- Server SAN:弄潮儿云计算时代
最初发表于<程序猿>2014年7每月一次. 4月30日本.Redhat公布1.71十亿收购Ceph开发商Inktank公司,加上之前2011年10月1.36十亿收购Gluster,Redh ...
- poj2096--Collecting Bugs(可能性dp第二弹,需求预期)
Collecting Bugs Time Limit: 10000MS Memory Limit: 64000K Total Submissions: 2678 Accepted: 1302 ...
- Event Sourcing - ENode(一)
分布式系统 摩尔定律如果一直能实现,不管是涉及或者实现一个OLTP的系统,我们是不是都会轻松点,用硬件堆就可以了.但是现在硬件已经在求变了,那么我们也得求变,云的概念如此之火,本质就是设施虚拟化,也可 ...
- Android 异步消息处理机制 让你在深入了解 Looper、Handler、Message之间的关系
转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 非常多人面试肯定都被问到过,请问And ...
- Model绑定
Model绑定 在前面的几篇文章中我们都是采用在URI中元数据类型进行传参,实际上ASP.NET Web API也提供了对URI进行复杂参数的绑定方式--Model绑定.这里的Model可以简单的理解 ...
- SQL 将URL编码转汉字!
原文:SQL 将URL编码转汉字! -- ============================================= -- 作 者: ruijc -- 描 述: 将Url编码转明文字符 ...
- NSIS:静默释放文件并运行 制作绿色单文件软件
原文 NSIS:静默释放文件并运行 制作绿色单文件软件 现在所谓的绿色单文件软件,大多与以下代码原理相似:把软件运行需要的文件封装为一个EXE文件,双击时释放到某个目录(大多是TEMP)并运行主程序文 ...
- 解决TD于ie10没有问题,
.打开命令提示符下,输入 gpedit 回车打开组策略编辑器: .计算机配置-管理模版-全部设置-关闭数据运行保护,双击打开.选择已启用.关闭组策略浏览器. .命令提示符下输入gpupdate/for ...