Learning PHP —— 設計模式 | Chap2:設計模式與UML

設計模式

設計模式基本原則

  • 設計原則 ① : 按接口而不是按實現來編程php

按接口而不是按實現編程是指,要將變量設置爲一個抽象類或接口數據類型的實例,
而不是一個具體實現的實例。這樣能夠將設計與實現解耦合。
有些語言變量聲明包含數據類型,例如在一個強類型語言中能夠有如下聲明:算法

<?php
    Interface IAlpha    //接口名
    class AlphaA implements IAlpha        //AlphaA的數據類型爲IAlpha
    Variable useAlpha    //聲明類型爲IAlpha,例如(IAlpha useAlpha)
    useAlpha    //實例化新的AlphaA()
?>

若是沒有強類型機制,能夠利用代碼提示保證按接口編程,以上一節寫過的一段代碼爲例編程

<?
//useProduct.php
class useProduct{
    public function __construct(){
        $apple = new FruitStore();
        $book = new BookStore();
        $this->doInterface($apple);
        $this->doInterface($book);
    }
    function doInterface(IProduct $product){
    //這裏的IProduct $product就是一種類型提示,方法類型提示爲接口IProduct
    //不管程序變得多複雜都不要緊,只要保證接口
    //就能夠作出任意的修改和增補,不會破壞程序的其它部分
        echo $product->apples();
        echo $product->books();
    }
}
$worker = new useProduct();
?>
  • 設計原則 ② : 應當優先選擇對象組合而不是類繼承設計模式

有的OOP程序猿認爲對象重用就等同於繼承,一個類能夠有大量屬性和方法,擴展這個類就能夠重用全部那些對象元素,而不用從新編寫代碼。能夠擴展類,再增長必要的新屬性和方法。下面將舉一個例子說明對象組合和類繼承之間的區別。app

首先是繼承的代碼框架

<?php
//Domath.php
class DoMath{
    private $sum;
    private $quotient;
    public function simpleAdd($first,$second){
        $this->sum = ( $first + $second );
        return $this->sum;
    }
    public function simpleDivide($divided, $divisor){
        $this->quotient = ( $dividend / $divisor);
        return $this->quotient;
    }
}
?>
<?php
//InheritMath.php
include_once('DoMath.php');
class InheritMath extends DoMath{
    private $textOut;
    private $fullFace;
    public function numToText($num){
        $this->textOut = (string)$num;
        return $this->textOut;
    }
    public function addFace($face,$msg){
        $this->fullFace = "<strong>" . $face . "</strong> : " .$msg;
        return $this->fullFace;
    }
}
?>
<?php
//ClientInherit
include_once('InheritMath.php');
class ClientInherit{
    private $added;
    private $divided;
    private $textNum;
    private $output;
    public function __construct(){
        $family = new InheritMath();
        $this->added = $family->simpleAdd(40,60);
        $this->divided = $family->simpleDivided($this->added,25);
        $this->textNum = $family->numToText($this->divided);
        $this->output = $family->addFace("Your results",$this->textNum);
        echo $this->output;
    }
}
$worker = new ClientInherit();
?>

下面是組合,Client類使用兩個不一樣的類,分別包含兩個方法,DoMath類等同於繼承例子中的父類,因此首先是DoText類ide

<?php
//DoText.php
//與InheritMath類類似,事實也如此,不過他沒有繼承DoMath類
class DoText{
    private $textOut;
    private $fullFace;
    public function numToText($num){
        $this->textOut = (string)$num;
        return $this->textOut;
    }
    public function addFace($face,$msg){
         $this->fullFace = "<strong>" . $face . "</strong> : " .$msg;
        return $this->fullFace;
    }
}
?>
<?php
//ClientCompost.php
include_once('DoMath.php');
include_once('DoText.php');
class ClientCompose{
    private $added;
    private $divided;
    private $textNum;
    private $output;
    public function __construct{
        $useMath = new DoMath();
        $useText = new DoText();
        $this->added = $useMath->simpleAdd(40,60);
        $this->divided = $useMath->simpleDivide($this->added,25);
        $this->textNum = $useText->numToText($this->divided);
        $this->output = $useText->addFace("Your results",$this->textNum);
        echo $this->output;
    }
}
?>

Client類必須包含多個類,看起來更勝一籌,不過在較大的程序中,組合能夠避免維護多個繼承層次上的各個子類,並且還能夠避免可能致使的錯誤。例如父類的一個改變會逐級向下傳遞給子類實現,這可能會影響子類使用的某個算法。
要避免使用繼承造成一長串子類、孫子類、曾孫子類,設計模式方法建議使用淺繼承。post

設計模式的組織

設計模式是按照做用和範圍來組織的

設計模式按做用能夠分爲如下3類ui

  • 建立型this

顧名思義,就是用來建立對象的模式,更確切地講,這些模式是對實例化過程的抽象
若是程序愈來愈以來組合,就會減小對硬編碼實例化的依賴,而更多地以來於一組靈
活的行爲,這些行爲能夠組織到一個更爲複雜的集合中,建立型模式提供了一些方法
來封裝系統使用的具體類的有關知識,還能夠隱藏實例建立和組合的相關信息

  • 結構型

這些模式所關心的是組合結構應當保證結構化。結構型類模式採用繼承來組合接口或實現結構型對象模式則描述了組合對象來創建新功能的方法。瞭解結構型模式對於理解和使用相互關聯的類(做爲設計模式中的參與者)頗有幫助

  • 行爲型

到目前爲止,絕大多數模式都是行爲型對象,這些模式的核心是算法和對象之間職責的分配。這些設計模式描述的不僅是對象或類的模式,它們還描述了類和對象之間的通訊模式

設計模式按範圍能夠分爲如下2類


  • 在兩類範圍中,第一類範圍是類。這些類模式的重點在於類及其子類之間的關係,類模式中的關係是經過繼承建成

  • 對象
    儘管大多數設計模式都屬於對象範圍,不過與類範圍中的那些模式同樣,不少模式也會使用繼承。對象設計模式與類模式的區別在於,對象模式強調的是能夠在運行時改變的對象,所以這些模式更具動態性。

設計模式做用、範圍和變化

clipboard.png

設計模式與框架有什麼不一樣

與框架相比,設計模式是體系結構中更小的元素,也更爲抽象,另外設計模式沒有框架那麼
特定。所以,設計模式更可重用,也比框架靈活。
框架的有點與模板有些相似:它們更有指示性,能夠更清楚地指示所解決問題的結構。爲了
提供這種易用性,它們不得不放棄了體系結構的靈活性。若是使用框架,構建應用會快得多,
可是所構建的應用會受到框架自己的約束。框架能夠包含面向對象結構,一般框架是分層的,
每一層處理更大設計中的一個方面。框架的一些特性在設計模式中也有體現,不過,設計模
式沒有框架那麼特定和具體,也沒有那麼龐大

UML

概念:UML(the Unified Modeling Language——統一建模語言),引入了一個強大的
圖形化的語法來描述面向對象系統

類圖

1.描述類

類是類圖的主要部分,類用帶有類名的方框來描述。

clipboard.png

上圖中,圖1最早顯示的是類的名稱,下面兩部分是可選的,用於顯示類名以外的信息。
能夠發現圖1已經足夠描述一些類,並不是總要在類圖中顯示每一個屬性和方法,甚至不須要
顯示每一個類

一般用斜體的類名(圖2),或者增長{abstract}到類名下(圖3)來表示該類是抽象
類。 第一種方法比第一種方法經常使用,可是當你作筆記時第二種方法更有用

接口定義的方式和類相同,但接口必須使用一個衍型(UML的一個擴展),如樓下所示

clipboard.png

2.屬性

通常來講,屬性用於描述一個類的屬性,屬性直接列在類名下面的格子中,如樓下所示
clipboard.png

屬性前面的符號表示該屬性可見性的級別或者是訪問控制
clipboard.png

冒號用於分隔屬性名和它的類型及默認值(默認值爲可選項,能夠不提供)
Again:只要提供必要的信息,不須要全部細節

3.操做

操做用於描述類方法,操做和屬性使用了類似的語法

(1)可見性符號放在方法名以前
(2)參數列表包含在括號之中
(3)方法若是有返回類型的話,用冒號來描述
(4)參數用逗號來分隔,而且遵照屬性語法
(5)參數名和它的數據類型間用冒號分隔

以下圖所示

clipboard.png

4.描述繼承和實現

繼承關係用從子類到父類的一條線來表示,線的頂端有一個空心閉合箭頭
clipboard.png

UML用"實現"來描述接口和實現接口的類之間的關係,若是ShopProduct類實現了Chargeable接口,就能夠加入類圖中,如圖所示
clipboard.png

5.關聯

一個類的屬性保存了對另外一個類的一個實例或多個實例的引用時,就產生了關聯
下圖中爲輛各種創建模型,並建立類之間的關聯,圖中指出Teacher對象擁有一個或多個對Pupil對象的引用,或者Pupil對象擁有一個或多個對Teacher對象的引用
clipboard.png

咱們能夠用剪頭來描述關聯的方向,若是Teacher類擁有Pupil類的一個實例,可是Pupil類並無Teacher類的實例,那咱們可讓關聯剪頭從Teacher類指向Pupil類,稱爲"單向關聯"
clipboard.png

若是兩個類剪互相擁有對方的引用,能夠用一個雙向剪頭來描述這種"雙向關聯"關係
clipboard.png

也能夠在關聯中指定一個類的實例被其它類引用的次數,能夠經過把次數或者範圍放在每一個類旁邊來講明。用星號(*)表示任意次數,以下圖所示,Teacher對象擁有零個或多個Pupil對象
clipboard.png

6.聚合和組合

都描述了一個類長期持有其它類的一個或多個實例的狀況,經過聚合和組合,被
引用的對象實例成爲引用對象的一部分。
聚合關係用一條以空心菱形開頭的線來講明,以下圖所示,定義了兩個類,
SchoolClass和Pupil, SchoolClass類聚合了Pupil
(意思就是學生組成了班級,若是咱們要刪除一個學校類,不須要同時刪除)

clipboard.png

組合是一個更強的關係,在組合中,被包含對象只能被它的容器所引用,當容器刪除
時,她應該也被刪除,組合關係用相似聚合關係的方式描述,但它的菱形是實心的
下圖中,Person類持有對SocialSecurityData對象的引用,而SocialSecurityData
對象實例屬於包含它的Person對象

clipboard.png

7.描述使用

使用:即依賴關係,並不是類之間的長久關係
下圖中,Report類使用了ShopProductWriter對象,這種使用關係由一條鏈接兩個類
的虛線和開放剪頭表示。Report類沒有把ShopProductWriter保存爲類中的屬性,
ShopProductWriter對象則將一組ShopProduct對象做爲屬性

clipboard.png

8.使用註解

註解:解釋類處理任務的過程(類圖只能捕捉系統結構)
以下圖,Report對象使用了ShopProductWriter,可是不知道具體如何實現,
咱們使用了註解來補充說明

clipboard.png

如圖所示,註解由一個這叫的方框組成,一般包含僞代碼片斷

時序圖

時序圖是基於對象而不是基於類的,用於爲系統中過程化的行爲建模。

以下,爲Report對象輸出產品數據的過程建模,從左到右展示了系統的參與者
咱們值使用類名來標記對象,若是圖中有同一個類的多個對象實例在獨立工做,
可使用label:class(例如product1 : ShopProduct)格式來包含對象名

clipboard.png

下圖咱們從上到下展現了該過程每一個對象的生命週期

clipboard.png

垂直的虛線是生命線,展現了系統中對象的生命週期,生命線上的矩形說明了過程當中的
焦點,即某個對象的激活期。
顯示對象間傳遞的消息,這個圖才更容易被看懂,以下

clipboard.png

箭頭表示消息從一個對象傳遞到另外一個對象,返回值通常不寫。
每一個消息都用相關的方法調用來標記,例如方括號說明一個條件,像
{okToPrint}
write()
只有在必定條件下write纔會被執行
星號用於表示一個重複的操做,能夠在方括號中進一步說明
*{for each ShopProduct}
write()

該圖含義:Report對象得到一個來自ProductStore對象的ShopProduct對象列表。
Report對象傳遞這個ShopProduct列表給一個ShopProcuctWriter對象,而
ShopProductWriter存放了對ShopProduct對象的引用(雖然咱們只能從圖中推斷
出折點)ShopProductWriter對象爲它引用的每一個ShopProduct對象調用ShopProduct::
getSummaryLine(),並添加執行結果到最終的輸出結果中

本文筆記參考書籍:
《深刻PHP:面向對象、模式與實踐》第4章
《Learning-PHP設計模式》第4章

下節:Chap3:建立型設計模式————工廠方法設計模式

相關文章
相關標籤/搜索