【編程課堂】Php設計模式(三):行爲型模式

在上一篇咱們講告終構型模式,結構型模式是討論類和對象的結構的。總共有7種。而今天咱們來介紹一下行爲型模式。php

1、什麼是行爲型模式?算法

一、設計模式:數據庫

是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計的總結。就好像杯子,是被前人設計出來的,實現了儲存水的功能,解決了人們的喝水問題。大多數人喝水都用杯子。可是你沒必要本身再重作另外方法再作一種容器,而實現的也是一樣的功能,只要會用別人作出來的杯子喝水就能達到目的。可是杯子有不少中,實現喝水的方式也不一樣,好比茶水杯子,咖啡杯,啤酒杯子等等,要選擇適合本身的杯子,就如茶水要用帶過濾網的杯子,若是用不帶過濾網的會喝一嘴茶葉。合適的纔是最好的。json

總結出來設計模式的特性以下:設計模式

1.廣泛性:通過前輩們的使用,大多人都實用的,總結提煉不斷的提高被廣泛認爲是實現某種事物的最有效的方法。session

2.封裝性:既然是方法那咱們就不用太關心細節是怎麼實現的,而主要是學習怎麼用這些方法模式,從而達到本身的目的。函數

3.面向對象性:指揮對象作事。把複雜問題簡單化。post

4.最優性:要充分相信適合的就是最好的。學習

二、面向對象:測試

或許你對面向對象還有疑問?指揮對象作事,把複雜問題簡單化。那麼咱們就來舉個例子說明。好比咱們去飯店吃飯,會叫服務員而後點菜,那咱們就是指揮服務員作事,至於服務員怎麼讓廚師作,廚師怎麼作,這些咱們都無論。咱們只管是否能吃到咱們叫到的菜。這個就是把復瑣事情簡單化了。咱們不用本身作菜,也不用知道菜怎麼作,咱們指揮服務員就好了,吃飯這件事就由複雜的作菜之類的變成吃這個簡單的事了。

三、行爲型模式:

就是描述類和對象之間的通訊和職責的。簡而言之,就是類和對象扮演什麼角色,還有怎麼扮演這個角色的問題。

2、行爲型模式的種類

大致上分爲三個大類:常見模式、已知模式、深度模式

常見模式包括: 模版方法模式命令模式 迭代器模式 觀察者模式 中介者模式 狀態式 職責鏈模式 策略模式

已知模式包括:備忘錄模式

深度模式包括:解釋器模式 訪問者模式

下面來介紹常見模式

Ø 常見模式

一、模版方法模式(Template):

定義一個操做中的算法骨架,而將一些實現步驟延遲到子類當中實現。就像一個豆漿機,無論放進去的是紅豆仍是黑豆,出來的都是豆漿。

好處:擴展性好,封裝不變的代碼,擴展可變的代碼

弊端:靈活性差,不能改變骨架部分。

應用場景:一類或一組具備共性的事物中

代碼實現

/**
*

  • 模板方法模式 Template
    */

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();

二、命令模式(Command) :

行爲請求者與行爲實現者解耦。就像軍隊裏的「敬禮」,不論是誰聽到這個命令都會作出標準的敬禮動做。

好處:便於添加和修改行爲,便於聚合多個命令

弊端:形成過多具體的命令類

應用場景:對要操做的對象,進行的相同操做

代碼實現
/**
*

  • 命令模式 Command
    */

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();
/**
*

  • 命令模式 Command
    */

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();
/**
*

  • 命令模式 Command
    *

*/
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();

三、迭代器模式(Iterator):

訪問聚合對象內容而不暴露內部結構。就像一個雙色球彩票開獎同樣,每次都是搖出七個球,不能能搖不是七個球的中獎號碼組合。

好處:以不一樣方式遍歷一個集合

弊端:每次遍歷都是整個集合,不能單獨取出元素

應用場景:須要操做集合裏的所有元素。

代碼實現:

/**
*

  • 迭代器模式 Iterator
    */

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();

四、觀察者模式(Observer):

又叫發佈訂閱模式,當一個主體對象發生改變時,依賴它的多個觀察者對象都獲得通知並自動更新響應。就像報社同樣,今天發佈的消息只要是看這份報紙的人看到的都是一樣的內容。若是發佈另外一份報紙,也是同樣的。

好處:廣播式通訊,範圍大,一呼百應,便於操做一個組團,「公有制」

弊端:不能單獨操做組團裏的個體,不能實行按需分配

應用場景:操做多個對象,並操做相同。

代碼實現:
/**

  • 優才網公開課示例代碼
    *

  • 觀察者模式 Observer
    */

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}" );
//實際應用會調用郵件發送服務如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();
/**
*

  • 觀察者模式 Observer
    */

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}" );
}
}
//給用戶發送訂單確認郵件 (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();

五、中介者模式(Mediator):

用中介對象封裝一系列的對象交互,中介使各對象不須要顯式地相互引用。相似於郵局,郵寄者和收件者不用本身跑很遠路,經過郵局就能夠。

好處:簡化了對象之間的關係,減小子類的生成

弊端:中介對象可能變得很是複雜,系統難以維護

應用場景:不須要顯示地創建交互

代碼實現:

/****/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();

相關文章
相關標籤/搜索