在上一篇我们讲了结构型模式,结构型模式是讨论类和对象的结构的。总共有7种。而今天我们来介绍一下行为型模式。
一、什么是行为型模式?
行为型模式就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。
二、行为型模式的种类
大体上分为三个大类:常见模式、已知模式、深度模式
模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态模式、职责链模式、策略模式
2、 已知模式包括:备忘录模式
3、深度模式包括:解释器模式 访问者模式
下面来介绍常见模式
Ø常见模式
1、模版方法模式(Template):
实现。 就像一个豆浆机,不管放进去的是红豆还是黑豆,出来的都是豆浆。
好处:扩展性好,封装不变的代码,扩展可变的代码。
弊端:灵活性差,不能改变骨架部分。
应用场景:一类或一组具有共性的事物中。
代码实现
练练手
/**
* 优才网公开课示例代码
*
* 模板方法模式 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) :
都会做出标准的敬礼动作。
好处:便于添加和修改行为,便于聚合多个命令。
弊端:造成过多具体的命令类。
应用场景:对要操作的对象,进行的相同操作。
代码实现
练练手
/**
* 优才网公开课示例代码
*
* 命令模式 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):
都是摇出七个球,不能能摇不是七个球的中奖号码组合。
好处:以不同方式遍历一个集合。
弊端:每次遍历都是整个集合,不能单独取出元素。
应用场景:需要操作集合里的全部元素。
代码实现
练练手
/**
* 优才网公开课示例代码
*
* 迭代器模式 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):
都得到通知并自动更新响应。就像报社一样,今天发布的消息只要 是看这份报纸的人看到的都是同样的内容。如果发布另一份报纸,也是一 样的。
好处:广播式通信,范围大一呼百应,便于操作一个组团,“公有制”。
弊端:不能单独操作组团里的个体,不能实行按需分配。
应用场景:操作多个对象,并操作相同。
代码实现
练练手
/**
* 优才网公开课示例代码
*
* 观察者模式 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();
5、中介者模式(Mediator):
用中介对象封装一系列的对象交互,中介使各对象不需要显式地相互引 用。
类似于邮局,邮寄者和收件者不用自己跑很远路,通过邮局就可以。
好处:简化了对象之间的关系,减少子类的生成。
弊端:中介对象可能变得非常复杂,系统难以维护。
应用场景:不需要显示地建立交互
代码实现
练练手
/**
* 优才网公开课示例代码
*
* 中介者模式 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();
6、状态模式(State) :
高兴了遛狗。在两种状态下变现出不同的行为。
好处:避免if语句实用,方便增加新状态,封装了状态转换规则。
弊端:增加系统类和对象的数量。
应用场景:用于对象的不同功能的转换。
代码实现
练练手
/**
* 优才网公开课示例代码
*
* 状态模式 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();
<?php
/**
* 优才网公开课示例代码
*
* 状态模式 State
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function output( $string ) {
echo $string . "n" ;
}
/**
*
* 定义一个电梯的接口
*/
abstract class LiftState{
//定义一个环境角色,也就是封装状态的变换引起的功能变化
protected $_context ;
public function setContext(Context $context ){
$this ->_context = $context ;
}
//首先电梯门开启动作
public abstract function open();
//电梯门有开启,那当然也就有关闭了
public abstract function close();
//电梯要能上能下,跑起来
public abstract function run();
//电梯还要能停下来,停不下来那就扯淡了
public abstract function stop();
}
/**
* 环境类:定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。
*/
class Context {
//定义出所有的电梯状态
static $openningState = null;
static $closeingState = null;
static $runningState = null;
static $stoppingState = null;
public function __construct() {
self:: $openningState = new OpenningState();
self:: $closeingState = new ClosingState();
self:: $runningState = new RunningState();
self:: $stoppingState = new StoppingState();
}
//定一个当前电梯状态
private $_liftState ;
public function getLiftState() {
return $this ->_liftState;
}
public function setLiftState( $liftState ) {
$this ->_liftState = $liftState ;
//把当前的环境通知到各个实现类中
$this ->_liftState->setContext( $this );
}
public function open(){
$this ->_liftState->open();
}
public function close(){
$this ->_liftState->close();
}
public function run(){
$this ->_liftState->run();
}
public function stop(){
$this ->_liftState->stop();
}
}
/**
* 在电梯门开启的状态下能做什么事情
*/
class OpenningState extends LiftState {
/**
* 开启当然可以关闭了,我就想测试一下电梯门开关功能
*
*/
public function close() {
//状态修改
$this ->_context->setLiftState(Context:: $closeingState );
//动作委托为CloseState来执行
$this ->_context->getLiftState()->close();
}
//打开电梯门
public function open() {
output( 'lift open...' );
}
//门开着电梯就想跑,这电梯,吓死你!
public function run() {
//do nothing;
}
//开门还不停止?
public function stop() {
//do nothing;
}
}
/**
* 电梯门关闭以后,电梯可以做哪些事情
*/
class ClosingState extends LiftState {
//电梯门关闭,这是关闭状态要实现的动作
public function close() {
output( 'lift close...' );
}
//电梯门关了再打开,逗你玩呢,那这个允许呀
public function open() {
$this ->_context->setLiftState(Context:: $openningState ); //置为门敞状态
$this ->_context->getLiftState()->open();
}
//电梯门关了就跑,这是再正常不过了
public function run() {
$this ->_context->setLiftState(Context:: $runningState ); //设置为运行状态;
$this ->_context->getLiftState()->run();
}
//电梯门关着,我就不按楼层
public function stop() {
$this ->_context->setLiftState(Context:: $stoppingState ); //设置为停止状态;
$this ->_context->getLiftState()->stop();
}
}
/**
* 电梯在运行状态下能做哪些动作
*/
class RunningState extends LiftState {
//电梯门关闭?这是肯定了
public function close() {
//do nothing
}
//运行的时候开电梯门?你疯了!电梯不会给你开的
public function open() {
//do nothing
}
//这是在运行状态下要实现的方法
public function run() {
output( 'lift run...' );
}
//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
public function stop() {
$this ->_context->setLiftState(Context:: $stoppingState ); //环境设置为停止状态;
$this ->_context->getLiftState()->stop();
}
}
/**
* 在停止状态下能做什么事情
*/
class StoppingState extends LiftState {
//停止状态关门?电梯门本来就是关着的!
public function close() {
//do nothing;
}
//停止状态,开门,那是要的!
public function open() {
$this ->_context->setLiftState(Context:: $openningState );
$this ->_context->getLiftState()->open();
}
//停止状态再跑起来,正常的很
public function run() {
$this ->_context->setLiftState(Context:: $runningState );
$this ->_context->getLiftState()->run();
}
//停止状态是怎么发生的呢?当然是停止方法执行了
public function stop() {
output( 'lift stop...' );
}
}
/**
* 模拟电梯的动作
*/
class Client {
public static function test() {
$context = new Context();
$context ->setLiftState( new ClosingState());
$context ->open();
$context ->close();
$context ->run();
$context ->stop();
}
}
Client::test();
7、职责链模式 (Chainof Responsibility):
机,不管那一台都可以取到钱。
好处:简单化对象隐藏链结构,便于添加新职责节点。
弊端:请求可能没有接受者,或者被多个接收者调用,性能降低。
应用场景:处理多种请求。
代码实现
练练手
/**
* 优才网公开课示例代码
*
* 职责链模式 Chain of Responsibility
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function output( $string ) {
echo $string . "n" ;
}
/**
* 加入在公司里,如果你的请假时间小于0.5天,那么只需要向leader打声招呼就OK了。
如果0.5<=请假天数<=3天,需要先leader打声招呼,然后部门经理签字。
如果3<请假天数,需要先leader打声招呼,然后到部门经理签字,最后总经经理确认签字,
如果请假天数超过10天,是任何人都不能批准的。
*/
/**
* 抽象处理者角色(Handler:Approver):定义一个处理请求的接口,和一个后继连接(可选)
*
*/
abstract class Handler
{
protected $_handler = null;
protected $_handlerName = null;
public function setSuccessor( $handler )
{
$this ->_handler = $handler ;
}
protected function _success( $request )
{
output(sprintf( "%s's request was passed" , $request ->getName()));
return true;
}
abstract function handleRequest( $request );
}
/**
* 具体处理者角色(ConcreteHandler:President):处理它所负责的请求,可以访问后继者,如果可以处理请求则处理,否则将该请求转给他的后继者。
*
*/
class ConcreteHandlerLeader extends Handler
{
function __construct( $handlerName ){
$this ->_handlerName = $handlerName ;
}
public function handleRequest( $request )
{
if ( $request ->getDay() < 0.5) {
output(sprintf( '%s was told' , $this ->_handlerName)); // 已经跟leader招呼了
return $this ->_success( $request );
}
if ( $this ->_handler instanceof Handler) {
return $this ->_handler->handleRequest( $request );
}
}
}
/**
* Manager
*
*/
class ConcreteHandlerManager extends Handler
{
function __construct( $handlerName ){
$this ->_handlerName = $handlerName ;
}
public function handleRequest( $request )
{
if (0.5 <= $request ->getDay() && $request ->getDay()<=3) {
output(sprintf( '%s signed' , $this ->_handlerName)); // 部门经理签字
return $this ->_success( $request );
}
if ( $this ->_handler instanceof Handler) {
return $this ->_handler->handleRequest( $request );
}
}
}
class ConcreteHandlerGeneralManager extends Handler
{
function __construct( $handlerName ){
$this ->_handlerName = $handlerName ;
}
public function handleRequest( $request )
{
if (3 < $request ->getDay() && $request ->getDay() < 10){
output(sprintf( '%s signed' , $this ->_handlerName)); // 总经理签字
return $this ->_success( $request );
}
if ( $this ->_handler instanceof Handler) {
return $this ->_handler->handleRequest( $request );
} else {
output(sprintf( 'no one can approve request more than 10 days' ));
}
}
}
/**
* 请假申请
*
*/
class Request
{
private $_name ;
private $_day ;
private $_reason ;
function __construct( $name = '' , $day = 0, $reason = '' ){
$this ->_name = $name ;
$this ->_day = $day ;
$this ->_reason = $reason ;
}
public function setName( $name ){
$this ->_name = $name ;
}
public function getName(){
return $this ->_name;
}
public function setDay( $day ){
$this ->_day = $day ;
}
public function getDay(){
return $this ->_day ;
}
public function setReason( $reason ){
$this ->_reason = $reason ;
}
public function getReason( ){
return $this ->_reason;
}
}
class Client {
public static function test(){
$leader = new ConcreteHandlerLeader( 'leader' );
$manager = new ConcreteHandlerManager( 'manager' );
$generalManager = new ConcreteHandlerGeneralManager( 'generalManager' );
//请求实例
$request = new Request( 'ucai' ,4, '休息' );
$leader ->setSuccessor( $manager );
$manager ->setSuccessor( $generalManager );
$result = $leader ->handleRequest( $request );
}
}
Client::test();
8、策略模式(Strategy):
队里的球员,场上的和场下休息的。教练可以让场上的下来,也 可以让场下的上阵。
好处:定义可重用的一系列算法和行为,并且消除了if else语句。
弊端:调用端必须知道所有策略类。
应用场景:用于对象间的替换。
代码实现
练练手
/**
* 优才网公开课示例代码
*
* 策略模式 Strategy
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function output( $string ) {
echo $string . "n" ;
}
//策略基类接口
interface IStrategy {
public function OnTheWay();
}
class WalkStrategy implements IStrategy {
public function OnTheWay() {
output( '在路上步行' );
}
}
class RideBickStrategy implements IStrategy {
public function OnTheWay() {
output( '在路上骑自行车' );
}
}
class CarStrategy implements IStrategy {
public function OnTheWay() {
output( '在路上开车' );
}
}
//选择策略类Context
class Context {
public function find( $strategy ) {
$strategy ->OnTheWay();
}
}
class Client {
public static function test(){
$travel = new Context();
$travel ->find( new WalkStrategy());
$travel ->find( new RideBickStrategy());
$travel ->find( new CarStrategy());
}
}
Client::test();
Ø已知模式
1、备忘录模式(Memento):
吗?
好处:给用户提供了一种可以恢复状态的机制。
弊端:消耗资源。
应用场景:用于需要保存的数据。
代码实现
练练手
/**
* 优才网公开课示例代码
*
* 备忘录模式 Memento
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function output( $string ) {
echo $string . "n" ;
}
class Originator { // 发起人(Originator)角色
private $_state ;
public function __construct() {
$this ->_state = '' ;
}
public function createMemento() { // 创建备忘录
return new Memento( $this ->_state);
}
public function restoreMemento(Memento $memento ) { // 将发起人恢复到备忘录对象记录的状态上
$this ->_state = $memento ->getState();
}
public function setState( $state ) { $this ->_state = $state ; }
public function getState() { return $this ->_state; }
public function showState() {
output( $this ->_state);
}
}
class Memento { // 备忘录(Memento)角色
private $_state ;
public function __construct( $state ) {
$this ->setState( $state );
}
public function getState() { return $this ->_state; }
public function setState( $state ) { $this ->_state = $state ;}
}
class Caretaker { // 负责人(Caretaker)角色
private $_memento ;
public function getMemento() { return $this ->_memento; }
public function setMemento(Memento $memento ) { $this ->_memento = $memento ; }
}
class Client {
public static function test(){
$org = new Originator();
$org ->setState( 'open' );
$org ->showState();
/* 创建备忘 */
$memento = $org ->createMemento();
/* 通过Caretaker保存此备忘 */
$caretaker = new Caretaker();
$caretaker ->setMemento( $memento );
/* 改变目标对象的状态 */
$org ->setState( 'close' );
$org ->showState();
/* 还原操作 */
$org ->restoreMemento( $caretaker ->getMemento());
$org ->showState();
}
}
Client::test();
return ;
try {
$db ->beginTransaction();
$succ = $db -> exec ( $sql_1 );
if (! $succ ) {
throw new Exception( 'SQL 1 update failed' );
}
$succ = $db -> exec ( $sql_2 );
if (! $succ ) {
throw new Exception( 'SQL 2 update failed' );
}
$succ = $db -> exec ( $sql_3 );
if (! $succ ) {
throw new Exception( 'SQL 3 update failed' );
}
$db ->commit();
} catch (Exception $exp ) {
$db ->rollBack();
}
Ø深度模式
1、解释器模式(Interpreter):
鞋都懂滴。
好处:可扩展性比较好,灵活性大
弊端:可能难以维护复杂的文法
应用场景:用于成对或者一对多的需求中
2、访问者模式(Visitor):
前提下定义作用于这些元素的新操作。如银行排号机。
好处:将相关的事物集中到一个访问者对象中。
弊端:增加新数据结构很困难。
应用场景:排队,排号。
三、总结
本篇介绍了行为型模式,行为模式涉及到算法和对象职责间的分配,行为类
模式采用继承机制在类间分派行为,TemplateMethod和Interpreter是类行为
模式。行为对象模式使用对象复合而不是继承,一些行为对象模式描述了一组相
互对等的对象如何相互协作以完成其中任何一个对象都单独无法完成的任务,如
Mediator在对象间引入一个mediator对象提供了松耦合所需的间接性;
Chain of Responsibility提供了更松的耦合,它通过一条候选对象链隐式的向一
个对象发松请求,可以运行时刻决定哪些候选者参与到链中;Observer定义并保
持了对象间的依赖关系;其它的行为对象模式常将行为封装封装在一个对象中,
并将请求指派给它,Strategy模式将算法封装在对象中,这样可以方面的改变和
指定一个对象所使用的算法;Command模式将请求封装在对象中,这样它就可
以作为参数来传递,已可以存储在历史列表中或以其它方式使用;State模式封
装一个对象的状态,使得当这个对象的状态对象变化时,该对象可改变它的行
为;Visitor模式封装分布于多个类之间的行为;而Iterator模式则抽象了访问和
遍历一个集合中对象的方式。