在上一篇我们讲了结构型模式,结构型模式是讨论类和对象的结构的。总共有7种。而今天我们来介绍一下行为型模式。
一、什么是行为型模式?
1、设计模式:
是一套被反复使用、多数人知晓的、经过分类编目的、代码设计的总结。就好像杯子,是被前人设计出来的,实现了储存水的功能,解决了人们的喝水问题。大多数人喝水都用杯子。但是你不必自己再重做另外方法再做一种容器,而实现的也是同样的功能,只要会用别人做出来的杯子喝水就能达到目的。但是杯子有很多中,实现喝水的方式也不同,比如茶水杯子,咖啡杯,啤酒杯子等等,要选择适合自己的杯子,就如茶水要用带过滤网的杯子,如果用不带过滤网的会喝一嘴茶叶。合适的才是最好的。
总结出来设计模式的特性如下:
1.普遍性:经过前辈们的使用,大多人都实用的,总结提炼不断的提升被普遍认为是实现某种事物的最有效的方法。
2.封装性:既然是方法那我们就不用太关心细节是怎么实现的,而主要是学习怎么用这些方法模式,从而达到自己的目的。
3.面向对象性:指挥对象做事。把复杂问题简单化。
4.最优性:要充分相信适合的就是最好的。
2、面向对象:
或许你对面向对象还有疑问?指挥对象做事,把复杂问题简单化。那么我们就来举个例子说明。比如我们去饭店吃饭,会叫服务员然后点菜,那我们就是指挥服务员做事,至于服务员怎么让厨师做,厨师怎么做,这些我们都不管。我们只管是否能吃到我们叫到的菜。这个就是把复杂事情简单化了。我们不用自己做菜,也不用知道菜怎么做,我们指挥服务员就行了,吃饭这件事就由复杂的做菜之类的变成吃这个简单的事了。
3、行为型模式:
就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。
二、行为型模式的种类
大体上分为三个大类:常见模式、已知模式、深度模式
常见模式包括: 模版方法模式命令模式 迭代器模式 观察者模式 中介者模式 状态式 职责链模式 策略模式
已知模式包括:备忘录模式
深度模式包括:解释器模式 访问者模式
下面来介绍常见模式
Ø 常见模式
1、模版方法模式(Template):
定义一个操作中的算法骨架,而将一些实现步骤延迟到子类当中实现。就像一个豆浆机,不管放进去的是红豆还是黑豆,出来的都是豆浆。
好处:扩展性好,封装不变的代码,扩展可变的代码
弊端:灵活性差,不能改变骨架部分。
应用场景:一类或一组具有共性的事物中
代码实现
<?php
/**
* 优才网公开课示例代码
*
* 模板方法模式 Template
*
* @author 优才网全栈工程师教研组
* @seehttp://www.ucai.cn
*/
function output($string) {
echo $string . "\n";
}
class Request {
public$token = '';
publicfunction __construct() {
$this->token ='0c6b7289f5334ed2b697dd461eaf9812';
}
}
class Response {
publicfunction render($content) {
output(sprintf('response-render: %s',$content));
}
publicfunction redirect($uri) {
output(sprintf('response-redirect: %s', $uri));
}
publicfunction json($data) {
output(sprintf('response-data: %s', json_encode($data)));
}
}
//父类,抽象类
abstract class Controller{
//封装了输入输出
protected$request;
protected$response;
//返回数据
protected$data = 'data';
publicfunction __construct($request, $response){
$this->request = $request;
$this->response = $response;
}
//执行请求函数,定义总体算法(template method),final防止被复写(不允许子类改变总体算法)
publicfinal function execute(){
$this->before();
if($this->valid()){
$this->handleRequest();
}
$this->after();
}
//定义hook methodbefore,做一些具体请求的前置处理
//非abstract方法,子类可以选择覆盖或不覆盖,默认什么都不做
protectedfunction before(){
}
//定义hook methodvalid,做请求的数据验证
//非abstract方法,子类可以选择覆盖或不覆盖,默认返回验证通过
protectedfunction valid(){
returntrue;
}
//定义hook methodhandleRequest,处理请求
//定义为abstract方法,子类必须实现或也声明为抽象方法(由子类的子类负责实现)
abstractfunction handleRequest();
//定义hook methodafter,做一些请求的后置处理
//非abstract方法,子类可以选择覆盖或不覆盖,默认直接输出数据
protectedfunction after(){
$this->response->render($this->data);
}
}
//子类1,实现父类开放的具体算法
class User extends Controller{
//覆盖before方法,实现具体算法,这是一个处理用户数据操作的控制器
//因此,我们选择在before里面判断用户是否已经登录了,这里简单判断下session数据
functionbefore(){
if(empty($_SESSION['auth'])){
//没登录就直接跳转了,不再执行后续的操作
$this->response->redirect("user/login.php");
}
}
//覆盖valid方法,这里我们验证用户提交数据中有没有带验证token
functionvalid(){
if(isset($this->request->token)){
return true;
}
returnfalse;
}
//覆盖handleRequest方法,必选,以为父类中声明了abstract了
functionhandleRequest(){
//做具体处理,一般根据参数执行不同的业务逻辑
}
//这个类我们选择不覆盖after方法,使用默认处理方式
}
//子类2,实现父类开放的具体算法
class Post extends Controller{
//这个类我们选择不覆盖before方法,使用默认处理方式
//这个类我们选择不覆盖valid方法,使用默认处理方式
//覆盖handleRequest方法,必选,以为父类中声明了abstract了
functionhandleRequest(){
//做具体处理,一般根据参数执行不同的业务逻辑
$this->data = array('title' => 'ucai');
}
//覆盖after方法,使用json格式输出数据
functionafter(){
$this->response->json($this->data);
}
}
class Client {
publicstatic 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 优才网全栈工程师教研组
* @seehttp://www.ucai.cn
*/
function output($string) {
echo $string . "\n";
}
class Document {
private$name = '';
public function __construct($name) {
$this->name = $name;
}
publicfunction showText() {
output(sprintf("showText: %s", $this->name));
}
publicfunction undo() {
output(sprintf("undo-showText: %s", $this->name));
}
}
class Graphics {
private$name = '';
publicfunction __construct($name) {
$this->name = $name;
}
publicfunction drawCircle() {
output(sprintf("drawCircle: %s", $this->name));
}
publicfunction undo() {
output(sprintf("undo-drawCircle: %s", $this->name));
}
}
class Client {
publicstatic function test() {
$document = newDocument('A');
$graphics = newGraphics('B');
$document->showText();
$graphics->drawCircle();
$document->undo();
}
}
Client::test();
<?php
/**
* 优才网公开课示例代码
*
* 命令模式 Command
*
* @author 优才网全栈工程师教研组
* @seehttp://www.ucai.cn
*/
function output($string) {
echo $string . "\n";
}
interface Command {
publicfunction execute();
publicfunction undo();
}
class Document implements Command {
private$name = '';
publicfunction __construct($name) {
$this->name = $name;
}
publicfunction execute() {
output(sprintf("showText: %s", $this->name));
}
publicfunction undo() {
output(sprintf("undo-showText: %s", $this->name));
}
}
class Graphics implements Command {
private$name = '';
publicfunction __construct($name) {
$this->name = $name;
}
publicfunction execute() {
output(sprintf("drawCircle: %s", $this->name));
}
publicfunction undo() {
output(sprintf("undo-drawCircle: %s", $this->name));
}
}
class Client {
publicstatic 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 优才网全栈工程师教研组
* @seehttp://www.ucai.cn
*/
function output($string) {
echo $string . "\n";
}
interface Command {
publicfunction execute();
publicfunction undo();
}
class Document {
private$name = '';
publicfunction __construct($name) {
$this->name = $name;
}
publicfunction showText() {
output(sprintf("showText: %s", $this->name));
}
publicfunction undo() {
output(sprintf("undo-showText: %s", $this->name));
}
}
class Graphics {
private$name = '';
publicfunction __construct($name) {
$this->name = $name;
}
publicfunction drawCircle() {
output(sprintf("drawCircle: %s", $this->name));
}
publicfunction undo() {
output(sprintf("undo-drawCircle: %s", $this->name));
}
}
class DocumentCommand implements Command {
private$obj = '';
publicfunction __construct(Document $document) {
$this->obj = $document;
}
publicfunction execute() {
$this->obj->showText();
}
publicfunction undo() {
$this->obj->undo();
}
}
class GraphicsCommand implements Command {
private$obj = '';
publicfunction __construct(Graphics $graphics) {
$this->obj = $graphics;
}
publicfunction execute() {
$this->obj->drawCircle();
}
publicfunction undo() {
$this->obj->undo();
}
}
class Client {
publicstatic 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(newGraphics('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 优才网全栈工程师教研组
* @seehttp://www.ucai.cn
*/
function output($string) {
echo $string . "\n";
}
class RecordIterator implements Iterator{
private$position = 0;
//注意:被迭代对象属性是私有的
private$records = array();
publicfunction __construct(Array $records) {
$this->position = 0;
$this->records = $records;
}
functionrewind() {
$this->position = 0;
}
functioncurrent() {
return$this->records[$this->position];
}
functionkey() {
return$this->position;
}
functionnext() {
++$this->position;
}
functionvalid() {
returnisset($this->records[$this->position]);
}
}
class PostListPager {
protected$record = array();
protected$total = 0;
protected$page = 0;
protected$size = 0;
publicfunction __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;
}
publicfunction getIterator() {
return newRecordIterator($this->record);
}
publicfunction getMaxPage() {
$max = intval($this->total /$this->size);
return $max;
}
publicfunction getPrevPage() {
return max($this->page - 1,1);
}
publicfunction getNextPage() {
return min($this->page + 1,$this->getMaxPage());
}
}
class Client {
publicstatic 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 优才网全栈工程师教研组
* @seehttp://www.ucai.cn
*/
function output($string) {
echo $string . "\n";
}
//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
class Order{
//订单号
private$id = '';
//用户ID
private$userId = '';
//用户名
private$userName = '';
//价格
private$price = '';
//下单时间
private$orderTime = '';
//订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
publicfunction __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{
publicfunction save($data){
returntrue;
}
}
class Client {
publicstatic 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} wasconfirmed!" );
//实际应用会调用邮件发送服务如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 优才网全栈工程师教研组
* @seehttp://www.ucai.cn
*/
function output($string) {
echo $string . "\n";
}
//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
class Order{
//订单号
private$id = '';
//用户ID
private$userId = '';
//用户名
private$userName = '';
//价格
private$price = '';
//下单时间
private$orderTime = '';
//订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
publicfunction __set($name, $value){
if(isset($this->$name)){
$this->$name = $value;
}
}
//获取订单属性
publicfunction __get($name){
if(isset($this->$name)){
return $this->$name;
}
return"";
}
}
//被观察者, 负责维护观察者并在变化发生是通知观察者
class OrderSubject implements SplSubject {
private$observers;
private$order;
publicfunction __construct(Order $order) {
$this->observers = new SplObjectStorage();
$this->order = $order;
}
//增加一个观察者
publicfunction attach(SplObserver $observer) {
$this->observers->attach($observer);
}
//移除一个观察者
publicfunction detach(SplObserver $observer) {
$this->observers->detach($observer);
}
//通知所有观察者
publicfunction notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
//返回主体对象的具体实现,供观察者调用
publicfunction getOrder() {
return$this->order;
}
}
//记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略
class ActionLogObserver implements SplObserver{
publicfunction update(SplSubject $subject) {
$order = $subject->getOrder();
//实际应用可能会写到日志文件中,这里直接输出
output( "[OrderId:{$order->id}] [UseId:{$order->userId}][Price:{$order->price}]" );
}
}
//给用户发送订单确认邮件 (UserMailObserver)
class UserMailObserver implements SplObserver{
publicfunction update(SplSubject $subject) {
$order = $subject->getOrder();
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output( "Dear {$order->userName}: Your order {$order->id} wasconfirmed!" );
}
}
//给管理人员发订单处理通知邮件 (AdminMailObserver)
class AdminMailObserver implements SplObserver{
publicfunction 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{
publicfunction save($data){
returntrue;
}
}
class Client {
publicstatic 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 = newUserMailObserver();
$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();
5、中介者模式(Mediator):
用中介对象封装一系列的对象交互,中介使各对象不需要显式地相互引用。类似于邮局,邮寄者和收件者不用自己跑很远路,通过邮局就可以。
好处:简化了对象之间的关系,减少子类的生成
弊端:中介对象可能变得非常复杂,系统难以维护
应用场景:不需要显示地建立交互
代码实现:
<?php
/**
* 优才网公开课示例代码
*
* 中介者模式 Mediator
*
* @author 优才网全栈工程师教研组
* @seehttp://www.ucai.cn
*/
function output($string) {
echo $string . "\n";
}
abstract class Mediator { // 中介者角色
abstractpublic function send($message,$colleague);
}
abstract class Colleague { // 抽象对象
private$_mediator = null;
publicfunction __construct($mediator) {
$this->_mediator = $mediator;
}
publicfunction send($message) {
$this->_mediator->send($message,$this);
}
abstractpublic function notify($message);
}
class ConcreteMediator extends Mediator { // 具体中介者角色
private$_colleague1 = null;
private$_colleague2 = null;
publicfunction send($message,$colleague) {
if($colleague == $this->_colleague1) {
$this->_colleague1->notify($message);
} else{
$this->_colleague2->notify($message);
}
}
publicfunction set($colleague1,$colleague2) {
$this->_colleague1 = $colleague1;
$this->_colleague2 = $colleague2;
}
}
class Colleague1 extends Colleague { // 具体对象角色
publicfunction notify($message) {
output(sprintf('Colleague-1: %s', $message));
}
}
class Colleague2 extends Colleague { // 具体对象角色
publicfunction notify($message) {
output(sprintf('Colleague-2: %s', $message));
}
}
class Client {
publicstatic 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技术文章
- PHP中session和cookie的区别
- php设计模式(一):简介及创建型模式
- php设计模式结构型模式
- Php设计模式(三):行为型模式
- 十款最出色的 PHP 安全开发库中文详细介绍
- 12个提问频率最高的PHP面试题
- PHP 语言需要避免的 10 大误区
- PHP 死锁问题分析
- 致PHP路上的“年轻人”
- PHP网站常见安全漏洞,及相应防范措施总结
- 各开源框架使用与设计总结(一)
- 数据库的本质、概念及其应用实践(二)
- PHP导出MySQL数据到Excel文件(fputcsv)
- PHP中14种排序算法评测
- 深入理解PHP原理之--echo的实现
- PHP性能分析相关的函数
- PHP 性能分析10则
- 10 位顶级 PHP 大师的开发原则
- 30条爆笑的程序员梗 PHP是最好的语言
- PHP底层的运行机制与原理
- PHP 性能分析与实验——性能的宏观分析
- PHP7 性能翻倍关键大揭露
- 鸟哥:写在PHP7发布之际一些话
- PHP与MySQL通讯那点事
- Php session内部执行流程的再次剖析
- 关于 PHP 中的 Class 的几点个人看法
- PHP Socket 编程过程详解
- PHP过往及现在及变革
- PHP吉祥物大象的由来
- PHP生成静态页面的方法
- 吊炸天的 PHP 7 ,你值得拥有!
- PHP开发中文件操作疑难问答
- MongoDB PHP Driver的连接处理解析
- PHP 杂谈《重构-改善既有代码的设计》之二 对象
- 在php中判断一个请求是ajax请求还是普通请求的方法
- 使用HAProxy、PHP、Redis和MySQL支撑10亿请求每周架构细节
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重构-改善既有代码的设计
- PHP场景中getshell防御思路分享
- 移动互联时代,你看看除了PHP你还会些什么
- 安卓系统上搭建本地php服务器环境
- PHP中常见的缓存技术!
- PHP里10个鲜为人知但却非常有用的函数
- 成为一名PHP专家其实并不难
- PHP 命令行?是的,您可以!
- PHP开发提高效率技巧
- PHP八大安全函数解析
- PHP实现四种基本排序算法
- PHP开发中的中文编码问题
- php.get.post
- php发送get、post请求的6种方法简明总结
- 中高级PHP开发者应该掌握哪些技术?
- 前端开发
- web前端知识体系大全
- 前端工程与性能优化(下)
- 前端工程与性能优化(上)
- 2016 年技术发展方向
- Web应用检查清单
- 如何成为一名优秀的web前端工程师
- 前端组件化开发实践
- 移动端H5页面高清多屏适配方案
- 2015前端框架何去何从
- 从前端看“百度迁徙”的技术实现(一)
- 从前端看“百度迁徙”的技术实现(二)
- 前端路上的旅行
- 大公司里怎样开发和部署前端代码?
- 5个经典的前端面试问题
- 前端工程师新手必读
- 手机淘宝前端的图片相关工作流程梳理
- 一个自动化的前端项目实现(附源码)
- 前端代码异常日志收集与监控
- 15年双11手淘前端技术总结 - H5性能最佳实践
- 深入理解javascript原型和闭包系列
- 一切都是对象
- 函数和对象的关系
- prototype原型
- 隐式原型
- instanceof
- 继承
- 原型的灵活性
- 简述【执行上下文】上
- 简述【执行上下文】下
- this
- 执行上下文栈
- 简介【作用域】
- 【作用域】和【上下文环境】
- 从【自由变量】到【作用域链】
- 闭包
- 完结
- 补充:上下文环境和作用域的关系
- Linux私房菜