在上一篇我們講了結構型模式,結構型模式是討論類和對象的結構的。總共有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私房菜