設計模式剖析

1、設計模式概述

設計模式對於工作一兩年的同學來講並不陌生,甚至覺得設計模式很高深,尤其對於新手來講,好像有些設計模式知道,一到實際應用中,又使用不上,久而久之,有一種"可遠觀而不可褻玩焉"的感覺。本文用另一種思路來分析設計模式,探究設計模式的奧秘。

1.1 何為設計模式

設計模式就是"設計的模式",前面是定語,是修飾,重點是模式,模式我們都知道,是一種可複製的經驗、經歷、榜樣,如"廣東模式"等。那為什麼要用模式呢?其一是前人有走過,後面不用再踩坑;二是具有可操作性,可直接復用。所以到這裡得出一個結論:模式是為了解決可復用性!

前面的設計到底是什麼意思?軟體設計、架構設計 ......,設計模式其實是一個簡寫,它的全稱是這樣的:設計模式-可復用面向對象軟體的基礎,到這裡就明白,這裡的設計其實是面向對象設計,說白了設計模式就是面向對象設計總結出來的模式集合。

1.2 設計模式到底要解決哪些問題

回答這個問題,至少有一個答案我們是知道,那就是復用性,因為模式是可復用的,除了這一點,其實還有一點,那就是可擴展性。為何這樣說,回到上面的分析上去,設計模式其實是面向對象設計總結出來的模式集合,面向對象主要解決什麼問題?答:可擴展性(上一篇文章有專門寫面向對象)。所以綜合一下,設計模式主要解決兩個問題:可復用性和可擴展性

1.3 設計模式本質

設計模式的本質就是利用面向對象的技術手段去達到設計的可擴展性和可復用性,所以面向對象是設計模式的內核,脫離了面向對象去談設計模式那是耍流氓的。大師又用簡單、經典的話概括出:找到變化、封裝變化!問題來了,那如何找到變化呢?又如何封裝變化呢?大師笑而不語。所以設計模式的本質就是:找到變化並封裝變化!(本質的東西是簡單的、通俗易懂的、美的,從本質入門,反推就可以尋找到答案了)

2、經典設計經驗

如何去評價一個設計是好的還是壞的呢?肯定有一個標準,那就是:高內聚、低耦合。它的意思是不同的功能之間耦合低,不要修改一個點就牽一髮而動全身,一個功能只完成它負責的職責,改動點也就是這一塊,不牽涉到其它的地方。

上面給出了評價的標準,那怎麼做具有可操作性呢?還是從這個話來分析:內聚是功能內聚,一個功能只專註它做的事,這個比較好理解,大師給它起了一個好聽名字叫單一職責;"獨木不成林,單絲不成線",事物之間具有關聯性,關聯就有產生耦合,完全獨立存在是沒有耦合的,這在現實中還是比較少,大部分的還是有關聯性的,關聯避免不了,那隻能讓關聯產生的耦合度變低,從這個意思出發,從面向對象中的剖析可以知道,相關聯的東西可能會變化(找到變化),如何去處理變化呢?面向對象給出了技術手段就是多態,所以答案已經呼之欲出了,低耦合的實現技術手段是多態(封裝變化)。

還記得上面的設計模式本質么?是找到變化、封裝變化,大師在面向對象中已經給出了答案。大師又身體力行,高度凝練出幾條設計經驗可以使得設計變得低耦合,個人覺得只要滿足下面幾條,就可以了,不像教科書上陳列出一大堆教條,反而讓人迷糊了,其實它們的本質作用是一致的。

  • 單一職責:這個不用講了,就是專註於一個功能。
  • 開閉原則:對內關閉修改,對外開放修改,這個很重要,不能修改原有的類,要修改說明不靈活,有變化就要修改原有的內容,然而對外開放就不一樣,有變化你就新增內容,不用對原有的邏輯有影響。這樣是不是耦合度變低了呢?開閉原則是依賴於下面的面向介面編程講的,開閉是目的,面向介面編程是實現手段。
  • 面向介面編程:或者是面向抽象編碼,意思是依賴於抽象,不依賴於具體的實現,如果與具體的實現相關聯性大,那麼靈活性不大,面向介面編程又與下面的優先使用組合配合使用。
  • 優先使用組合:這裡還是比較好理解的,事物之間有關聯,一個類中包含另外一個類,又考慮變化,就用一個介面來封裝變化,有新變化,按照介面實現一個具體的實現即可。

至此,設計模式中經常使用到的手段就是介面編程和組合,也即設計模式核心問題解決的方案都是在面向對象中。

3、設計模式實戰

假如有這樣的一個需求:不同部門列印需求不同,宣傳部門要求加上頁頭和頁首,業務部門要求加上水印(請勿外傳字樣)......,請設計一個列印系統。

3.1 渣渣的設計

拿到這個需求,發現不同部門的列印需求是不一樣,所以一個簡單的設計方案出來了,設計一個列印介面,不同的部門去實現這個介面類,如果有新部門增加,再寫一個實現類就可以了。假如一個部門又改了列印需求,怎麼辦?再需要重新寫一個類。

// 列印介面

interface Print{

public void print();

}

// 宣傳部門實現

XuChuangDepartment implements Print{

public void print(){

。。。

}

}

// 業務部門實現

YeWuDepartment implements Print{

public void print(){

。。。

}

}

這樣的設計問題在於,實現的類過多,可復用性不好,劃分的粒度太大了。

3.2 小白的設計

小白拿到這個需求,想到列印分為列印頁頭、頁尾、頁內容和其它附屬的內容(如水印或者背景圖片),想到了模板的方法,寫下如下代碼:

// 列印模板類

abstract Print(){

abstract void printHeader();

abstract void printFooter();

abstract void printBody();

abstract void printNewRequired();

void print(){

printHeader();

printBody();

printFooter();

printNewRequired();

}

}

// 宣傳部門實現

XuChuangDepartment extends Print{

printHeader(){

。。。

}

printFooter(){

。。。

}

printBody(){

。。。

}

printNewRequired(){

。。。

}

}

這裡的設計問題與渣渣的設計問題一樣,如果自己部門再提列印需求,要麼修改現有的代碼,要麼重新實現一個類,粒度還是過於粗糙。

3.3 老白的設計

老白彷彿看到了問題所在,在前面的基礎上進行修改,他是這樣設計的,把列印頁頭、頁尾設計成介面,這樣的粒度就變小了,偽代碼如下:

class Print{

IPrintHeader printHeader;

IPrintFooter printFooter;

IPrintBody printBody;

IPrintOther printOther;

void print(){

if(printHeader != null){

printHeader.print();

}

。。。

}

}

老白設計其實也不是很好,正確的做法是用裝修模式來設計,最基本的列印需求就是列印出內容,其它的頁頭、頁尾、水印都是附加功能。

4、總結

設計模式的本質就是找到變化、封裝變化,所以在創建型、結構型、行為型設計模式中,它們都是在找到變化並封裝變化,封裝的手法綜合了組合、繼承、多態來實現,靈活地運用設計模式可以使得設計變得更靈活性,即可擴展性,重要的話說三遍:可擴展性、可擴展性、可擴展性!

推薦閱讀:

螞蟻金服技術專家分享:如何在三年內快速成長為一名技術專家
推薦一篇技術乾貨,關於數據分析平台的架構與設計

TAG:設計模式 | 面向對象分析與設計 | 系統架構 |