设计模式(注重理解为什么),
設計模式
- 設計模式
- 定義
- 反模式
- 閱讀前須知
- 好處
- 設計模式分類:
- 分類意義
- 分類
- 常見疑問:
- 六大原則總結:
- 別人總結:
- 我的總結:
- 單例模式,策略模式
- 單件模式的定義:
- 場景
- 為什么要使用單件模式:
- 為什么要只創建一個對象:
- 為什么使用單件模式不使用全局變量:
- 單例模式常見問題
- 策略模式(代碼復用)
- 場景
- 策略模式用到的原則:(為什么)
- 策略回顧遇到的問題
- 觀察者模式:
- 定義(發布與訂閱)
- 場景
- 原則:(為什么)
- 回顧時存在的疑問
- 裝飾者模式
- 定義
- 場景
- 原則
- 需注意的地方:
- 工廠模式
- 定義
- 場景
- 為什么
- 常見疑惑
- 抽象工廠模式
- 定義
- 場景
- 原則
- 工廠方法與抽象工廠的聯系和區別:
- 聯系:
- 作用:
- 區別
- 命令模式
- 定義
- 場景(為什么)
- 實現解耦(幫助理解)
- 常見疑問
- 適配器與外觀模式
- 適配器,裝飾者,外觀的作用(區別
- 定義
- 場景
- 常見疑問
- 對象適配器和類適配器
- 裝飾者和適配器
- 外觀模式
- 定義
- 原則
- 常見問題
- 外觀模式回顧存在疑問
- 模板方法模式(代碼復用)
- 定義
- 場景
- 策略模式和模板方法
- 原則
- 常見疑問
- 迭代器與組合模式
- 定義
- 常見疑問
- 場景
- 常見疑問
- 組合模式
- 定義
- 場景
- 常見疑問
- 幫助理解對答:
- 狀態模式
- 定義
- 場景
- 策略模式和狀態模式
- 常見疑問
- 代理模式
- 定義
- RMI (遠程代理)
- 虛擬代理
- 相關場景:設置CD封面的虛擬代理
- 核心代碼:
- 常見疑問:
- 代理和裝飾者的圍爐夜話:
- 保護代理
- 相關類圖:
- 相關步驟:
- 核心代碼:
- 常見疑問
- 其他類型代理
- 復合模式
- 定義
- 一群模式攜手合作(不是復合)
- 常見疑問:
- 關于組合模式中的安全性和透明性:
- MVC
- MVC與Web
- 其他模式
- 橋接
- 生成器
- 責任鏈
- 蠅量
- 解釋器
- 中介者
- 備忘錄
- 原型
- 訪問者
本文章參考自headfirst設計模式,如有侵權請聯系作者
看完這本書心得:該書主要就是教如何設計出更有彈性的系統。沒想到花了十天才把這本書看完。這本書我覺得只要有面向對象基礎都可以嘗試看看。可以長長見識,耗時不多。
最大作用:幫助理解源碼的含義,以及為何這么用。
設計模式
定義
模式實在某情境下,針對某問題的某種解決方案。
(模式必須應用一個重復出現的問題,即可以多次使用,像什么破窗找東西就不實際)
反模式
定義:反模式告訴你如何采用一個不好的解決方案解決一個問題。
作用:1. 反模式告訴你為何這個解決方案從長遠看會造成不好的影響。
2. 除了告訴你什么解決方案不好之外,也會向你建議一些會引向好的解決方案的可能性。閱讀前須知
開發的目標就是簡單(簡單的方式行得通,滿足需求,就別用模式),過度使用設計模式可能導致代碼被過度工程化,模式會帶來復雜性(產生過多的類和對象),模式只是指導方針,讓設計變得更簡單和系統更具備彈性(殺雞焉用宰牛刀),
所有的設計都應該盡量保持簡單,只有在需要實踐擴展的地方,才值得使用復雜性模式
總結:總是使用滿足需要的最簡單解決方案,不管它用不用模式,讓設計模式自然而然地出現在你的設計中,而不是為了使用而是用。并且牢記:你所遇到大多數的模式都是現有模式的變體,而非新模式。
好處
好處:
1.松耦合,建立彈性系統-》加快開發速度(因為松耦合,前后端可以同時進行)->分工明確,前端只負責前端,后端只負責后端。
2.共享詞匯(一聽就明白)
我建立的這個廣播類,他會持續地追蹤所有傾聽他的對象,只要有數據進來,就會把消息發送給每一個傾聽者。最棒的地方在于傾聽者可以在任何時候加入這個廣播類,也可以在任何時候從廣播中刪除。而這個廣播類本身并不用知道這些傾聽者的確切類,只要實現了正確的接口,就可以當傾聽者。 —》只要說觀察者就行了,簡單清晰精確。
設計模式分類:
分類意義
怎么分類不重要,重要的是了解這些模式和它們之間的關系。分類,然后好比較可讓你對模式有清晰的概念
就比如汽車也分為:跑車,經濟車,卡車……,一旦有了分類或類目,你就可以方便地這么說:“如果你想從硅谷開車到圣克魯斯,那么跑車將會是最好的選擇”。或者“因為石油的市場狀況日益惡化,所以應該購買經濟車,比較省油。
通過分類,我們可以將一組模式視為一個群體。當我們需要一個創建型模式,但又不知掉確切是哪一個的時候,就可以用創建型模式這個詞來統稱它。
而且分類也有助于我們比較相同類目內的其他成員。比方說:“迷你車是最具有風格的小型車”。或者幫助我們縮小縮小范圍,”我需要一部省油的車子“。
再者說,對于改變對象接口來說,適配器模是最好的結構型模式。
并且,類目還可以開發新的領域,比方說”我們真的想要開發一部跑車,具有法拉利的性能和Miata的價格“.(壽命短之類的缺陷)
所以類目可以讓我們思考模式群組之間的關系,以及同一組模式內模式之間的關系,還可以讓我們找出新的模式。但是為什么只是用三個類目呢?
(就像夜晚天空中的星星一樣,你可以看見許多類目。“三”是一個適當的數目,并且是有許多人所決定出來的數目,他有助于更好地進行模式分類,但是的確有人建議用四個,五個或更多)
分類
結構型:通過用接口將實現與抽象聯系起來的方式把已有對象組合起來進行建模(把類或對象組合到更大的結構中,[以獲取新的結構或功能])。(外觀與適配器、代理模式、裝飾器模式、橋接模式、組合模式、享元模式)
行為型:(涉及類和對象如何交互以及分配職責。[對象之間的溝通與互連])通過對變化進行封裝使得所建立的模型可以提供靈活的行為方式。(策略與觀察者、模板方法模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式)
創建型:將對象實例化,這類模式都提供一個方法,將客戶從所需要實例化的對象解耦(單例模式、抽象工廠與工廠方法、建造者模式、原型模式)
常見疑問:
問:為何裝飾者模式被歸類為結構類目中?不應該是行為類目嗎?
答:是的,許多人都這么認為,四人組(設計成這樣的四個人)的想法:結構型模式用來描述類和對象如何被組合以建立新的結構或新的功能。裝飾者模式允許你通過“將某對象包裝進另一個對象的方式",來組合對象以提供新的功能.所以焦點是在于如何動態地組合對象以獲取功能,而不是行為型模式的目的——對象之間的溝通與互連。請牢記,這幾個模式的意圖并不相同,而這通常是了解某個模式屬于哪個類目的關鍵。
其實還有兩類:并發型模式和線程池模式,用一個圖片來整體描述一下:
轉至:
設計模式的三大分類
六大原則總結:
別人總結:
六大設計原則:
單一職責
開閉原則
里氏替換原則:
迪米特法則(即下面的最少知識原則):
只與你的直接朋友交談,不跟“陌生人”說話
其含義是:如果兩個軟件實體無須直接通信,那么就不應當發生直接的相互調用,可以通過第三方轉發該調用。其目的是降低類之間的耦合度,提高模塊的相對獨立性。
迪米特法則要求限制軟件實體之間通信的寬度和深度,正確使用迪米特法則將有以下兩個優點。
- 降低了類之間的耦合度,提高了模塊的相對獨立性。
- 由于親合度降低,從而提高了類的可復用率和系統的擴展性。
缺點:
過度使用迪米特法則會使系統產生大量的中介類,從而增加系統的復雜性,使模塊之間的通信效率降低。所以,在釆用迪米特法則時需要反復權衡,確保高內聚和低耦合的同時,保證系統的結構清晰。
從迪米特法則的定義和特點可知,它強調以下兩點:
- 從依賴者的角度來說,只依賴應該依賴的對象。
- 從被依賴者的角度說,只暴露應該暴露的方法。
接口隔離原則:
定義:
1、客戶端不應該依賴它不需要的接口。
2、類間的依賴關系應該建立在最小的接口上。
以上兩個定義的含義是:要為各個類建立它們需要的專用接口,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調用。
接口隔離原則和單一職責的區別:
接口隔離原則和單一職責都是為了提高類的內聚性、降低它們之間的耦合性,體現了封裝的思想,但兩者是不同的:
- 單一職責原則注重的是職責,而接口隔離原則注重的是對接口依賴的隔離。
- 單一職責原則主要是約束類,它針對的是程序中的實現和細節;接口隔離原則主要約束接口,主要針對抽象和程序整體框架的構建。
https://www.jianshu.com/p/3232c9891403
我的總結:
抽象倒置原則
自己總結:
所有的原則都應該在有幫助的時候才遵守。所有的設計都不免需要折衷(在抽象和速度之間取舍,在空間和時間之間平衡……)。雖然原則提供了方針,但在采用原則之前,必須全盤考慮所有的因素。
松耦合(面向接口,增加復用潛力,依賴于抽象而不依賴于具體,降低對象之間的依賴,一個類應當只對應一個職能,),來建立彈性的OO系統,提高系統的可維護性。 松耦合-》易復用,易擴展,還可以加快效率(易多人開發)
易擴展:在不修改現有代碼的情況下,可搭配新的行為。
常用到的設計原則:
//在設計模式中,所謂“實現一個接口”并“不一定”表示“寫一個類,并利用implement關鍵詞來實現某個java接口”。”實現一個接口“泛指“實現某個超類型(可以是類或者接口)的某個方法”。
多用組合,少用繼承 (java不支持多重繼承。使用類繼承難松耦合);
封裝變化。(狀態,策略)
開放-關閉原則(對擴展開放,對修改關閉,結合上面的原則)(裝飾者)
單一責任:一個類應該只有一個引起變化的原因。
(內聚:一個類或一個模塊被設計成只支持一組相關的功能)
當我們允許一個類不但要完成自己的事情(管理某種內聚),還同時要擔負更多的責任(例如遍歷)時,我們就給了這個類兩個變化的原因。即當這個集合改變的話這個類也必須改變,遍歷的方式改變的話,這個類也必須改變。
最少知識原則(得墨忒耳法則):只和你的密友談話。(只和朋友交談) 外觀模式跟最少知識原則的關系
解釋:當你正在設計一個系統,不管是任何對象,你都要注意它所交互的類有哪些,并注意它和這些類是如何交互的。
這個原則希望我們在設計中,不要讓太多的類耦合在一起,免得修改系統中一部分,會影響到其他部分。
好萊塢原則:別調用(打電話給)我們,我們會調用(打電話給)你。
作用:**防止“依賴腐敗”**的方法。如:當高層組件依賴低層組件,而低層組件又依賴高層組件,而高層組件又依賴邊側組件,而邊側組件又依賴低層組件時,依賴腐敗就發生了。這樣的情況,是無法分清系統是如何設計的。
要依賴抽象,不要依賴具體類。(依賴倒置原則)(抽象工廠)
為交互對象之間的松耦合設計而努力。(觀察者)
單例模式,策略模式
單例懶漢式代碼
** DCL(double check lock)模式為什么要使用volatile,防止半初始化。 **
如T t = new T() ; 在匯編中有如下幾步:
當jvm進行優化發生指令重排序(對于單線程不影響最后結果,代碼執行順序會發生改變),即第3個指令會與第4個交換,多線程中會出現半初始化問題,一般情況在百萬數據情況下不會出現,以上就可能出現。
單件模式的定義:
確保類只有一個實例,并提供一個全局訪問點。
場景
注冊表等只需要創建一個類的地方。
為什么要使用單件模式:
為什么要只創建一個對象:
避免程序的行為異常。比如注冊表,多了會導致配置紊亂。再者printer spooler,計算機可以使用多個打印機,但當存在兩個printer spooler就會出現打印錯誤,比如同時使用一個打印機則打印出錯。
為什么使用單件模式不使用全局變量:
節省資源,延遲實例化,全局變量在一開始就要求實例化。
再者無法確保只創建一個實例 如:慎用全局變量,再則全局變量也會變相鼓勵開發人員,用許多全局變量指向許多小對象來造成命名空間的污染。單件不鼓勵這樣的現象,但單件仍然可能被濫用
。
單例模式常見問題
策略模式(代碼復用)
描述了怎樣按需要在一組可替換的算法中選用算法,幾把所定義的一些算法各自封裝起來,可根據客戶的需要分別使用它們。該模式可使算法獨立變化而不影響他的客戶。該模式既注重算法使用的靈活性,又注重對需求的變化性。
場景
需要擴展類但又不想全部繼承其方法如鴨子類,不同類型鴨子有不同的quack和fly方式。
策略模式用到的原則:(為什么)
(解決了繼承復用代碼功能,但不需要全部繼承,就是因為針對變化的部分)
策略回顧遇到的問題
1.怎么實現的?(將經常變化的地方封裝[拿出來作為接口],在讓其他類實現[方便擴展],然后再在Context類中組合進去【即添加變化類的接口】,在調用即可)
//比如duck類中的fly經常需要修改或者擴展。,就把fly封裝起來,讓接口去實現。 flyBehavior //Context類 public abstract class Duck{FlyBehavior flyBehavior;public Duck(){}public void peformFly(){flyBehavior.fly();//fly行為由被封裝為flyBehavior}public void setFlyBehavior(FlyBehavior flyBehavior){ this.flyBehavior = flyBehavior;} //也可以放到構造器中,不過就不夠動態 } //ContextConcrete類 public class ModelDuck extends Duck{FlyBehavior flyBehavior;public ModelDUck(){flyBehavior = new FlyNoWay();// fly:I can't fly;} } //封裝變化為接口 public interface FlyBehavior{void fly(); } //接口實現 public class FlyNoWay implements FlyBehavior {public void fly(){System.out.println("I can't fly");} } public class FLyRocketPowered implements FLyBehavor{public void fly(){System.out.println("I'm flying with a rocket");} }//Test public class Test{public static void main(String args[]){Duck model = new ModelDuck();model.performFly(); //i can't fly.model.setFlyBehavior(new FlyRocketPowered());model.performFly(); // i'm flying with a rocket.} }觀察者模式:
定義(發布與訂閱)
用于定義對象間的一對多的依賴關系,當一個對象發生變化時,所有依賴他的對象都將得到通知并自動更新。
以下是兩種形式:建議使用第一種方式(subject)。
第二種方式(Observable)違背了針對接口編程以及面向組合而非繼承的原則。但java內置支持。
場景
出版(subject(接口)/observable(類))與訂閱(observer)
報紙出版者和訂閱者的關系。
原則:(為什么)
為了交互對象之間的松耦合設計而努力。
回顧時存在的疑問
怎么拉而不是自動強行推送??
在update方法的形參改為Subject/Observable,將Subject的變量用get公開獲取。(這也是拉的缺點,數據被公開獲取)
對比推/拉代碼即可觀察出,
推送是將所有參數發過去,update中接受所有參數。
拉是傳遞一個Subject/Observable對象,并且要讓其參數變量可以公開獲取
拉:
class CurrentCondition implements Observer,DisplayElement{ void update(Subject subject,Object args){//WeatherDate entends Subjectif(sub instanceOf WeatherData){WeatherData weatherData = (WeatherData) sub;//通過get獲取相關參數this.temperature = weatherData.getTemperature()j;//...this.humidity = weatherData.getHumidity(); display();//就是一個輸出獲取參數值的作用} }}推:
class CurrentCondition implements Observer,DisplayElement{ void update(double temperature,double humidity,double pressure){//通過推送過來的值進行賦值this.temperature = weatherData.getTemperature();//...this.humidity = weatherData.getHumidity(); display();//就是一個輸出獲取參數值的作用} }}class WeatherData implements Subject{private ArrayList observers;private double temperature;//...//...others needs parameterspubic WeatherData(){observers = new ArrayList();}void notifyObservers(){//可以加些限制條件 改變值大于多少才會通知,在setMeasurements方法中設置即可。for (Observer obs:observers){obs = observers.get(i);obs.update(temperature,...) //推送直接將所有參數發送過去}} }裝飾者模式
定義
裝飾者模式動態地將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
// Beverage beverage = new Milk(new Milk(new Espresso())); 可以用無數個裝飾者類包裝組件
具體實現:
場景
星巴克Beverage(飲料)進行擴展。 (類數量太多,價格變動)
原則
類應該對擴展開放,對修改關閉。(開放-關閉原則)
缺點:引入新的抽象層次,增加代碼的復雜度,。
需注意的地方:
1.難以理解 2.類型問題 3.因為組合原因會增加大量小對象導致程序很復雜。 以及包裝過多。
工廠模式
定義
? 工廠方法模式定義了一個創建對象的接口,但由子類決定實例化的類是哪一個。工廠方法讓類把實例化推遲到子類。
(將實例化放到一個抽象方法中,當需要創建時,才調用create,子類實現create)(原本是直接根據參數進行if判斷)(但其實感覺差不多,就是添加種類時,可以到具體子類中修改)
理解:工廠方法讓子類決定要實例化的類是哪一個。容易理解錯誤-》所謂的“決定”,并不是指模式允許子類本身在運行時做決定,而是指在編寫創建者類時,不需要知道實際創建的產品時哪一個。選擇了使用哪個子類,自然就決定了實際創建的產品時什么。
(比如:pizzastore時抽象類,不知道是選擇CaliforniaPizzaStore子類還是NewYorkPizzaStore子類中的那種類型pizza(cheese,pepperoni)。
為什么使用工廠模式?
將創建對象代碼集中起來,方便維護,將客戶代碼和真實的實現解耦。(針對接口實現)
場景
2.簡單工廠不是一種設計模式,更像一種編程習慣。(經常被用到) (就是將需要new的地方法全部放在SimpleFactory種,讓它來create()創建。
1.需要new的地方,將其用放到抽象方法中讓子類去實現,不同子類實現不同特點的new 產品(加盟店)。
為什么
為什么在工廠模式代碼中仍要new?對象創建是現實的,如果不創建任何對象,就無法創建java程序。
1.將創建對象代碼集中起來,方便維護,將客戶代碼和真實的實現解耦。(針對接口實現)(通過將創建代碼放入抽象方法中,讓子類實現create)
常見疑惑
抽象工廠模式
定義
抽象工廠模式提供一個接口,用于創建相關或依賴對象的家族,而不需要明確指定具體類。
場景
披薩配料工廠。
原則
要依賴抽象,不要依賴具體類。(依賴倒置原則)
要滿足該原則應盡量(有足夠理由時,可以違反。比如一個不太會改變的類,直接實例化具體類也沒什么大礙,比如String)以下三點:
1.變量不可以持有具體類的引用(使用工廠)
2.不要讓類派生自具體類 (派生具體類,就會依賴于具體類。盡量派生自一個抽象[接口或抽象類])
3.不要覆蓋基類中已實現的方法。 (如果覆蓋基類已實現的方法,那么你的基類就不是一個真正適合被繼承的抽象。基類中已實現的方法,應該由所有子類共享)
不使用工廠模式時,pizzastore依賴于任何一個具體的pizza類,每新增一個種類,就多一個依賴。
工廠方法與抽象工廠的聯系和區別:
聯系:
抽象工廠的方法經常以工廠方法的方式實現。
抽象工廠的任務是定義一個負責創建一組產品的接口。這個接口內的每個方法都創建一個具體產品,同時我們利用實現抽象工廠的子類來提供這些具體的做法。所以,在抽象工廠中利用工廠方法實現生產方法是相當自然的做法。
簡單工廠,雖然不是真正的設計模式,但仍不失為一個簡單的方法,可以將客戶程序從具體類解耦。
作用:
這兩種模式以及簡單工廠都可以將對象的創建封裝起來,從而減少應用程序和具體類之間的依賴,以便于得到更松耦合、更有彈性的設計。
區別
工廠方法使用繼承:把對象的創建委托給子類,子類實現工廠方法來創建對象。
抽象工廠使用對象組合:對象的創建被是現在工廠接口所暴露出來的方法中。
內容為按照headfirst 設計模式.順序學習(除非特意學習某個模式)
命令模式
定義
命令模式將“請求"封裝成對象,以便使用不同的請求、隊列或者日志來參數化其他對象。命令模式也支持可撤銷的操作。
場景(為什么)
當需要“發出請求的對象”和“接受與執行這些請求的對象”解耦時使用命令模式
1.接待員(接受訂單)與廚師:接待員無需知道訂單內容的含義
2.遙控器與電燈打開(或者其他功能):遙控器無需知道按鈕的實現(意義)。直接按就行。(將遙控器和電燈對象解耦)
從而不用不用寫出這樣的代碼:
? if(slot1 = Light) then light.on(),
? else if(slot1 ==Hottub) then hottob.jetsOn(),
? else if… //這樣的代碼每次添加新的物品都必需修改代碼,這會造成潛在的錯誤,而且工作沒完沒了。(為什么)
3.日志恢復(通過命令模式記錄保存之前的操作,在電腦死機時可以成批依次地調用命令進行恢復)以及事務系統(原子性,全做或全不做)
實現解耦(幫助理解)
遙控器和電燈(功能對象)完全解耦,即使再新的廠商對象也無需在RemoteControl中修改代碼(NullCommand可以用作空對象[不實現功能的按鈕],用于做擴展)
常見疑問
1.如何實現多次撤銷?
使用棧保存之前的命令。每次撤銷都是彈出最上面的命令(最后的一次操作)。
適配器與外觀模式
適配器,裝飾者,外觀的作用(區別
適配器將一個對象包裝起來以改變接口;
裝飾者將一個對象包裝起來以增加新的行為和責任;
外觀將一群對象”包裝“起來以簡化其接口。
定義
適配器模式將一個類的接口,轉換成客戶期望的另一個接口。適配器讓原本接口不兼容的類可以合作無間
場景
TurkeyAdapter:讓火雞能使用鴨子的方法。
插座適配,將兩口和適配器配合變成三口(類似轉接器的作用)
還可以用于兼容適配,比如 之前用枚舉器,現在要用新的迭代器,但是也希望以前的代碼也使用迭代器這時候就可以使用適配器適配兼容。
常見疑問
一個適配器需要做多少“適配”的工作?如果我需要實現一個很大的目標接口,似乎有“很多”工作要做?
的確如此。實現一個適配器所需要進行的工作,的確和目標接口的大小成正比。如果不用適配器,就必須改寫客戶端的代碼來調用這個新的接口,將會花費許多力氣來做大量的調查工作和代碼改寫工作。
一個適配器只能夠封裝一個類?
適配器模式的工作是將一個接口轉換成另一個。雖然大多數的適配器模式所采取的例子都是讓一個適配器包裝一個被適配者。但我們知道這個世界其實復雜多了,所以你可能遇到一些狀況,需要讓一個適配器包裝多個被適配者。這涉及另一個模式——外觀模式。人們常常將外觀模式和適配器模式混為一談。
萬一我的系統中新舊并存,那應該保存哪個?還是不用適配器更好?
可以創建一個雙向適配器,支持兩邊的接口。 (實現涉及的兩個接口即可)
對象適配器和類適配器
裝飾者和適配器
外觀模式
定義
外觀模式提供了一個統一的接口,用來訪問子系統中的一群接口。外觀定義了一個高層接口,讓子系統更容易使用。
原則
最少知識原則(得墨忒耳法則):只和你的密友談話。
解釋:當你正在設計一個系統,不管是任何對象,你都要注意它所交互的類有哪些,并注意它和這些類是如何交互的。
這個原則希望我們在設計中,不要讓太多的類耦合在一起,免得修改系統中一部分,會影響到其他部分。
如何遵循此原則?
只應該調用屬于以下范圍的方法:
采用最少知識原則有什么缺點嗎?
是的,雖然這個原則減少了對象之間的依賴,研究顯示這會減少軟件的維護成本;但是采用這個原則也會導致更多的”包裝“類被制造出來,以處理和其他組件的溝通,這可能會導致復雜度和開發時間的增加,并降低運行時的性能。
常見問題
如果外觀封裝了子系統的類,那么需要底層功能的客戶如何接觸這些類?
外觀沒有”封裝“子系統的類,只是提供簡化的接口。所以客戶如果覺得有必要,依然可以直接使用子系統的類。即外觀模式可以在提供簡化接口的同時,依然將系統完整的功能暴露出來,以供有需要的人使用。
一個子系統可以有多個外觀。
除了能夠提供一個比較簡單的接口之外,外觀模式還有其他優點嗎?
外觀模式允許你將客戶實現從任何子系統中解耦。比方說:你得到了大筆加薪,所以想要升級你的家庭影院,采用全新的和以前不一樣接口的組件。如果當初你的客戶代碼是針對外觀而不是針對子系統編寫的,現在就不需要改變客戶代碼,只需要修改外觀代碼(而且有可能廠商會提供新版的外觀代碼)。
我可不可以這樣說,適配器模式和外觀模式的差異在于:適配器包裝一個類,而外觀可以代表許多類?
不對!提醒你,適配器模式將一個或多個類接口變成客戶所期望的一個接口。雖然大多數教科書所采用的例子中適配器只適配一個類,但是你可以適配許多類來提供一個接口讓客戶編碼。類似地,一個外觀也可以只針對一個擁有復雜接口地類提供簡化的接口。兩種模式的差異,不在于他們”包裝"了及各類,而在于他們的意圖。適配器模式的意圖是,”改變“接口來符合客戶的期望;而外觀模式的意圖是,提供子系統的一個簡化接口。
外觀模式回顧存在疑問
1.外觀模式是怎么只提供一個接口的?最少知識原則又跟它有什么關系?
模板方法模式(代碼復用)
定義
模板方法模式在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。
(即定義一個算法步驟,讓子類為一個或多個步驟提供實現)
場景
咖啡和茶的制作方式相似,則可以將相同地方提取出來,不同的地方抽象,泛化名稱,放到咖啡因飲料(基類)的一個制作方法中。
數組的sort方法也是模板方法。 需要子類實現Comparable中的CompareTo
為什么說數組的sort也算是一個模板方法?
這個模式的重點在于提供一個算法,并讓子類實現某些步驟而數組的排序做法很明顯地并非如此!但是我們都知道,荒野中地模式并非總是如同教科書例子一般地中規中矩,為了符合當前地環境和實現地約束,他們總是要被適當地修改。這個Array類sort()方法的設計者受到一些約束。通常我們無法設計一個類繼承Java數組,而sort()方法下能夠適用于所有的數組(每個數組都是不同的類).所以他們定義了一個靜態方法,而由被排序的對象內的每個元素自行提供比較大小的算法部分。所以,這雖然不是教科書上的模板方法,但它的實現仍然符合模板方法的精神。再者,由于不需要繼承數組就可以使用這個算法,這樣使得排序變得更有彈性、更有用。
策略模式和模板方法
排序實現實際上看起來更像是策略模式,而不是模板方法模式,為什么歸為模板方法模式?
你之所以會這么認位,可能是因為策略模式使用對象組合,在某種程度上,你是對的——我們使用數組對象排序我們的數組,這部分和策略模式非常相似。但是請記住,在策略模式中你所組合的類實現了整個算法。數組所實現的排序算法并不完整,它需要一個類填補compareTo()方法的實現。因此我們認為這更像模板方法。
兩者對話:
| 我定義一個算法家族,并讓這些算法可以互換。正因為每一個算法都被封裝起來了,所以客戶可以輕易使用地不同的算法。 | |
| 嘿!聽起來好像是我在做的事情。但是我的意圖和你不一樣:我的工作是要定義一個算法的大綱,而由我的子類定義其中某些步驟的內容。這么一來,我在算法中的個別步驟可以由不同的實現,但是算法的結構維持不變。而你似乎必須要放棄對算法的控制。 | |
| 我不確定話可以這么說……更何況,我并不是使用繼承進行算法的實現,我是通過對象組合的方式,讓客戶可以選擇算法實現。 | |
| 這我記得。但是我對算法有更多的控制權,而且不會重復代碼,事實上,除了極少一部分之外,我的算法的每一個部都是相同的,所以我的類比你的有效率得多。會重復使用到的代碼,都被我放進了超類中,好讓所有的子類共享。 | |
| 你或許更有效率一點(只是一點點),也的確需要更少的對象。和我所采用的委托模型比起來,你也沒有那么復雜。但是因為我使用對象組合,所以我更有彈性。利用我,客戶就可以在運行時改變他們的算法,而客戶所需要做的,只是改用不同的策略對象罷了。拜托作者選擇把我擺在第一章,這不是沒有道理的! | |
| 好吧,我真替你感到高興,但是你別忘了,環顧四周,我可是最常被使用的模式。為什么呢?因為我在超類中提供了一個基礎的方法,達到代碼的復用,并允許子類指定行為。我相信你會看到這一點在創建框架時是非常棒的! | |
| 也許呢……但是,別忘了依賴!你的依賴程度比我高。 | |
| 這話怎么說?我的超類時抽象的。 | |
| 但是你必須依賴超類中的方法的實現,因為這是你算法中的一部分。但我就不同了,我不依賴任何人;整個算法我自己搞定吧! |
其他例子:java.io的InputStream類有一個read()方法,是由子類實現的,而這個方法又會被read(byte b[],int off,int len)模板方法使用。
原則
好萊塢原則:別調用(打電話給)我們,我們會調用(打電話給)你。
作用:**防止“依賴腐敗”**的方法。如:當高層組件依賴低層組件,而低層組件又依賴高層組件,而高層組件又依賴邊側組件,而邊側組件又依賴低層組件時,依賴腐敗就發生了。這樣的情況,是無法分清系統是如何設計的。
做法:低層組件可以參與計算,低層組件不可以直接調用高層組件,高層組件控制何時以及如何讓低層組件參與。
好萊塢原則和依賴倒置原則之間的關系如何?
依賴倒置原則教我們盡量避免使用具體類,而多使用抽象。而好萊塢原則是用在創建框架或組件上的一種技巧,好讓低層組件能夠被掛鉤進計算中,而且又不會讓高層組件依賴低層組件。**兩者的目標都在于解耦,但是依賴倒置原則更加注意如何在設計中避免依賴,好萊塢原則則教我們一個技巧,創建一個有彈性的設計,允許低層結構能夠互相操作,而又防止其他類太過依賴他們。
低層組件不可以調用高層組件的方法嗎?
并不盡然。事實上,低層組件在結束時,常常會調用從超類中繼承來的方法。我們所要做的是,避免讓高層和低層組件之間有明顯的環狀依賴
常見疑問
什么時候使用抽象方法,什么時候使用鉤子(hook)?
當子類必須提供某個方法或步驟的實現時,使用抽象方法。如果算法中的某個部分是可選的,那么這個部分可以使用鉤子,子類可以實現這個鉤子,也可以選擇不實現。
使用鉤子的真正目的?
1)讓子類實現算法中可選的部分。(或者在鉤子對于子類的實現并不重要的時候)
2)讓子類能夠有機會對模板方法中即將發生的(或剛剛發生的)步驟作出反應。比如說,名為justReOrderedList()的鉤子方法允許子類在內部列表重新組織后執行某些動作(例如在屏幕上重新顯示數據)。鉤子也可以讓子類有能力為其抽象類做一些決定。
子類必須實現抽象類中所有的方法。
似乎我應該保持抽象方法的數目越少越好,否則,在子類中實現這些方法將會很麻煩?
可以讓算法內的步驟不要切割的太細。但是如果步驟太少的話,會比較沒有彈性,要自己判斷折衷,也請記住,某些步驟是可選的,所有你可以將這些步驟是現成鉤子,而不是實現成抽象方法,這樣就可以讓抽象類的子類減輕負擔。
迭代器與組合模式
定義
描述:提供一個方式來遍歷集合,而無需暴露集合的實現
迭代器模式提供一種方法順序訪問一個聚合對象中的各個元素,但又不暴露其內部的表示。
(將調用者和集合操作【遍歷】解耦)
常見疑問
1.讓方法不生效:throw new lang.UnsupportedOperationException。
2.在多線程的情況下,可能會由多個迭代器引用同一個對象集合。remove()會造成怎樣的影響?
后果并沒有指明,所以很難預料。當你的程序在多線程的代碼中使用到迭代器時,必須特別小心。
場景
在使用ArrayList和數組的情況下的集合代碼都可以通過迭代器來循環不需要寫兩遍循環代碼,且實現其接口還可以隱藏內部表示。
常見疑問
我聽說過“**內部的”迭代器和“外部的”的迭代器。**這是什么?我們在前面例子中實現的是哪一種?
我們實現的是外部的迭代器,也就是說,客戶通過調用next()取得下一個元素。而內部的迭代器則是由迭代器自己控制。你必須將操作傳入給迭代器比外部迭代器更沒有彈性。然而,某些人可能認為內部的迭代器比較容易使用,因為只需將操作告訴它,它就會幫你做完所有事情。
對于散列表這樣的集合,元素之間并沒有明顯的次序關系,我們該怎么辦?
迭代器意味著沒有次序(java.util.Enumeration是一個有次序的迭代器實現)。只是取出所有的元素,并不表示取出元素的先后就代表元素的大小次序。對于迭代器來說,數據結構可以是有次序的,或者是沒有次序的,甚至數據可以是重復的。除非某個集合的文件有特別說明,否則不可以對迭代器所取出的元素大小順序做出假設。
組合模式
定義
描述:客戶可以將對象和集合以及個別對象一視同仁
組合模式允許你將對象組合成樹形結構來表現“整體/部分”層次結構。組合能讓客戶以一致的方式處理個別對象以及對象組合。
場景
在使用ArrayList和數組的情況下的集合代碼都可以通過迭代器來循環不需要寫兩遍循環代碼,且實現其接口還可以隱藏內部表示。在這樣的組合下,再在這數組或ArrayList中添加子數組或者子ArrayList。
常見疑問
組件、組合、樹?我被搞混了?。
組合包含組件。組件有兩種:組合和葉節點元素。聽起來像遞歸是不是?組合有一群孩子,這些孩子可以時別的組合或者葉節點元素。當你用這種方式組織數據的時候,最終會得到樹形結構(正確的說法是由上而下的樹形結構),根部是一個組合,而組合的分支逐漸往下延伸,直到葉節點為止。
組合跟迭代器又有什么關系?
合作無間。
代碼舉例:
我:到底怎么回事?首先說“一個類一個責任”,現在卻給我們一個讓一個類有兩個責任的模式。組合模式不但要管理層次結構,而且還要執行菜單的操作。
設計者:你的觀察有幾分真實性。我們可以這么說,組合模式以單一責任設計原則換取透明性。什么是透明性?通過讓組件的接口同時包含一些管理子節點和葉節點的操作,客戶就可以將組合和葉節點一視同仁。(無法統一處理菜單和菜單項就會失去透明性)即看不見-》不可見。
也就是說,一個元素究竟是組合還是葉節點,對客戶是透明的。 現在我們在MenuComponent類中同時具有兩種類型的操作(例如試圖把菜單添加到菜單項),所以我們失去了一些“安全性"這是設計上的抉擇;我們當然也可以采用另一種方向的設計,**將責任區分開來放在不同的接口中。**這么一來,設計上就比較安全,但我們也因此失去了透明性,客戶的代碼將必須用條件語句和instanceof操作符處理不同類型的節點。 所以,回到你的問題,這是一個很典型的折衷案例。==盡管我們受到設計原則的指導,但是,我們總是需要觀察某原則對我們的設計所造成的影響。有時候,我們會故意做一些看似違反原則的事情。==然而,在某些例子中,這是觀點的問題;比方說,讓管理孩子的操作(例如add(),remove(),getChild())出現在葉節點中,似乎很不恰當,但是換個角度看,你可以把葉節點視為沒有孩子的節點。
幫助理解對答:
狀態模式
定義
定義:狀態模式允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了他的類.
解釋:==狀態模式將狀態封裝成為獨立的類,并將動作委托到代表當前狀態的對象,我們知道行為會隨著內部狀態而改變。比如糖果機,在NoQuarterState或HasQuarterState兩種狀態時,投入25分錢會有不同的行為。==一個對象“看起來好像修改了它的類”是什么意思呢?從客戶視角來看:如果說你使用的對象能夠完全改變它的行為,那么你會覺得這個對象實際上是從別的類實例化而來的。然而,實際上,你知道我們是在使用組合通過簡單引用不同的狀態對象來造成類改變的假象。
描述:封裝基于狀態的行為,并將行為委托到當前狀態(對象)。
封裝:將每個狀態封裝進一個類-》改變局部化(不用到大量if中改變)
場景
糖果機根據相應行為進入相應的狀態。
不使用狀態模式:(要擴展時混亂):
使用狀態模式:
解決的問題:
策略模式和狀態模式
基本常識:策略模式和狀態模式是雙胞胎,在出生時才分開。你已經知道了,策略模式是圍繞可以互換的算法來創建業務的。狀態走的是更崇高的路,狀態模式通過改變對象內部的狀態來幫助對象控制自己的行為。
策略模式和狀態模式有相同的類圖,但他們的意圖不同。
狀態:
策略:
區別:
策略模式通常會用行為或算法來配置Context類。狀態模式允許Context隨著狀態改變而改變行為。
策略:有一個可以實例化的類,而且通常給它一個實現某些行為的策略對象。
狀態: Context對象會隨著時間而改變狀態,而任何的狀態改變都是定義好的。
常見疑問
在GumballMachine中,狀態決定了下一個狀態應該是什么。ConcreteState總是決定接下來的狀態是什么嗎?
不,并非總是如此,Context也可以決定狀態轉換的流向。一般來說,當狀態轉換是固定的時候,就適合放在Context;然而,當轉換是更動態的時候,通常會放在狀態類中(例如,在GumballMachine中,由運行時糖果的數目決定狀態要轉換倒NoQuarter還是SoldOut)。將狀態轉換放在狀態類中的缺點是:狀態類之間產生了依賴。在我們的GumballMachine實現中,我們試圖通過使用Context上的getter方法把依賴減到最小,而不是顯示硬編碼具體狀態類。請注意,在做這個決策的同時,也等于是在為另一件事情做決策:當系統進化時,究竟哪個類是對修改封閉(Context還是狀態類)的。
客戶會直接和狀態交互嗎?
不會。狀態是用在Context中來代表它的內部狀態以及行為的,所以只有Context才會對狀態提出請求。客戶不會直接改變Context的狀態。全盤了解狀態是Context的工作,客戶根本不了解,所以不會直接和狀態聯系。
如果在我的程序中Context有許多實例,這些實例之間可以共享狀態對象嗎?
是的,絕對可以,事實上這是很常見的做法。但唯一的前提是,你的狀態對象不能持有它們自己的內部狀態;否則就不能共享。想要共享狀態,你需要把每個狀態都指定到靜態的實例變量中。如果你的狀態需要利用到Context中的的方法或者實例變量,你還必須在每個handler()方法內傳入一個context的引用。
使用狀態模式似乎總是增加我們設計中類的數目。請看GumbleMachine的例子,新版本比舊版本多出了許多類!
沒錯。**在個別的狀態類中封裝狀態行為,結果總是增加這個設計中類的數目。這就是為了要獲取彈性而付出的代價。**除非你的代碼是一次性的,可以用完就扔掉(是呀!才怪!),那么其實狀態模式的設計是絕對值得的。其實真正重要的是你暴露給客戶的類數目,而且我們有辦法將這些額外的狀態類全都隱藏起來。
讓我們看一下另一種做法:如果你有一個應用,他有很多狀態,但是你決定不將這些狀態封裝在不同的對象中,那么你就會得到巨大的、整塊的條件語句。這會讓你的代碼不容易維護和理解。通過使用許多對象,你可以讓狀態變得很干凈,在以后理解和維護它們時,就可以省下很多的工夫。
狀態模式類圖顯示State是一個抽象類,但你不是使用接口實現糖果機狀態的嗎?
是的。如果我們沒有共同的功能可以放進抽象類中,就會使用接口。在你實現狀態模式時,很可能想使用抽象類。這么一來,當你以后需要在抽象類中加入新的方法時就很容易,不需要打破具體狀態的實現。
我們為什么需要WinnerState?為什么不直接在SoldState中發放兩顆糖果?
這個一個好問題。這兩個狀態幾乎一樣,唯一的差別在于,WinnerState狀態會放兩顆糖果。你當然可以將發放兩顆糖果的代碼在SoldState中,當然這么做有缺點,因為你等于是將兩個狀態用一個狀態類來代表。這樣做你犧牲了狀態類的清晰易懂來減少一些冗余代碼。你也應該考慮到在前面的章節中所學到原則:一個類,一個責任。將WinnerState狀態的責任放進SoldState狀態中,你等于是讓SoldState狀態具有兩個責任。那么促銷結束之后或者贏家的幾率改變之后,你又該怎么辦呢?所以,這必須用你的智慧來做折衷。
代理模式
定義
代理模式為另一個對象提供一個替身或占位符以控制對這個對象的訪問。
描述:包裝另一個對象,并控制對它的訪問。
相關類圖:
RMI (遠程代理)
步驟:
1.制作一個遠程接口Remote(MyRemote implements Remote),再添加客戶需要調用的方法
2.制作遠程接口的實現。(MyRemoteImpl Extends UnicastRemoteObject implements MyRemote)
? 然后再通過注冊服務{
? try{ MyRemote service = new MyRemoteImpl();
? Naming.rebind(“RemoteHello”,service);} catch(Exception ex){…}
3.產生Stub類和Skeleton類。 cmd: %rmic MyRemoteImpl
4.執行remiregistry。 開啟注冊表用于給客戶端尋找相應的stub類,然后stub類將請求發送給Skeleton,然后Skeleton打包(序列化)將相應方法的返回值返回,stub在解包(反序列化)將結果返回給客戶端。
ps:對于RMI,程序員最常犯的三個錯誤:
1)忘了啟動遠程服務之前先啟動rmiregistry(要用Naming.rebind()注冊服務,rmiregistry必須是運行的)
2)忘了讓變量和返回值的類型成為可序列化的類型(這種錯誤無法在編譯器發現,只會在運行時發現)。
3)忘了給客戶提供stub類。
ps:關于客戶如何取得stub類?
我聽說,在Java 5 ,甚至連stub都不需要產生了,這是真的嗎?
? 是真的。Java 5的RMI和動態代理搭配使用,動態代理動態產生stub,遠程對象的stub是java.lang.reflect.Proxy實例(連同一個調用處理器),它是自動產生的,來處理所有把客戶的本地調用變成遠程調用的細節。所以,你不再需要使用rmic,客戶和遠程對象溝通的一切都在幕后處理掉了。
5.啟動服務 %java MyRemoteImpl
從哪里啟動?可能是從你的遠程實現類中的main()方法,也可能是從一個獨立的啟動類。在這個簡單的例子中,我們是從實現類中的main()方法啟動的,先實例化一個服務對象,然后到RMI registry中注冊(Naming.rebind())。
虛擬代理
虛擬代理控制訪問創建開銷大的資源。
大概步驟:當對象在創建前和創建中時,由虛擬代理來扮演對象的替身。對象創建后,代理就會將請求直接委托給對象。(可以防止程序被掛起)
相關場景:設置CD封面的虛擬代理
核心代碼:
常見疑問:
看起來,遠程服務器和虛擬服務器差異非常大,它們真的是一個模式嗎?
在真實的世界里,代理模式有許多變體,這些變體都有共通點:都會將客戶對主題施加的方法調用攔截下來。
代理將客戶從ImageIcon解耦了,如果它們之間沒有解耦,客戶就必須等到每幅圖像都被取回,然后才能把它繪制在界面上。代理控制ImageIcon的訪問,以便在圖像完全創建之前提供屏幕上的代表。一旦ImageIcon被創建,代理就允許訪問ImageIcon。
我們要如何讓客戶使用代理,而不是真正的對象?
好問題。一個常用的技巧是提供一個工廠,實例化并返回主題。因為這是在工廠方法內發生的,我們可以用代理包裝主題在返回,而客戶不知道也不在乎他使用的代理還是真東西。
我已經知道代理和裝飾者的關系了,但是適配器?代理和適配器也很類似。
代理和適配器都是擋在其他對象的前面,并負責將請求轉發給他們。適配器會改變對象適配的接口,而代理則實現相同的接口。有一個額外相似性牽涉到保護代理。保護代理可以根據客戶的角色來決定是否允許客戶訪問特定的方法。所以保護代理可能只提供給客戶部分接口,這就和適配器很相像了(適配器是只能訪問共同接口的方法)。
代理和裝飾者的圍爐夜話:
保護代理
相關類圖:
相關步驟:
核心代碼:
創建InvocationHandler,invoke的實現代碼:
常見疑問
到底“動態代理”動態在哪里?是不是指在運行時才將它實例化并和handler聯系起來?
不是的。動態代理之所以被稱為動態,是因為運行時才將它的類創建出來。代碼開始執行時,還沒有proxy類,它是根據需要從你傳入的接口集創建的。
InvocationHandler看起來像一個很奇怪的proxy。它實現所代理的類的任何方法。?
這是因為InvocationHandler根本就不是proxy,它只是一個幫助proxy的類,proxy本身是利用靜態的Proxy.newProxyInstance()方法在運行時動態地創建地。
可以通過isProxyClass()的返回值true判斷一個類是動態代理類。
為什么使用skeleton?我以為我們早在java1.2就已經擺脫了skeleton了。
確實,我們不需要真的產生skeleton,因為java1.2RMI利用reflectionAPI直接將客戶調用分派給原稱為服務。盡管如此,我們還是希望呈現skeleton,因為這可以幫助你從概念上理解內部的機制。
其他類型代理
防火墻代理(Firewall Proxy):控制網絡資源的訪問,保護主題免于“壞客戶”的侵害。
智能引用代理(Smart Reference Proxy):當主題被引用時,進行額外的動作,例如計算一個對象被引用的次數。
緩存代理(Caching Proxy):為開銷大的運算結果提供暫時存儲:它也允許多個客戶共享結果,以減少計算或網絡延遲。(Web服務器代理,內容管理與出版系統)
同步代理(Synchronization Proxy):在多線程的情況下為主題提供安全的訪問。(出現在JavaSpaces,為分散式環境內的潛在對象集合提供同步訪問控制)
復雜隱藏代理(Complexity Hiding Proxy):用來隱藏一個類的復雜集合復雜度,并進行訪問控制。有時候也稱為外觀代理。復雜隱藏代理和外觀模式是不一樣的,因為代理控制訪問,而外觀模式只提供另一組接口。
寫入時復制代理(CopyOnWrite Proxy):用來控制對象的復制,方法是延遲對象的復制,知道客戶真的需要為止。這是虛擬代理的變體。(CopyOnWriteArrayList)
如有侵權請聯系本網站!
復合模式
定義
復合模式結合兩個或以上的模式,組成一個解決方案,解決一再發生的一般性問題。
(一般是多個模式解決一個問題,而不是一個模式解決一個問題然后加起來。)
一群模式攜手合作(不是復合)
鴨子 →鵝\xrightarrow{鵝}鵝?適配器→計算呱呱叫的次數,不修改原有代碼\xrightarrow{計算呱呱叫的次數,不修改原有代碼}計算呱呱叫的次數,不修改原有代碼?裝飾者→每次創建都要包裝\xrightarrow{每次創建都要包裝}每次創建都要包裝? 使用抽象工廠將創建和包裝放在一個方法中→管理一群對象\xrightarrow{管理一群對象}管理一群對象?迭代器和組合模式→持續觀察某些對象\xrightarrow{持續觀察某些對象}持續觀察某些對象?觀察者模式
常見疑問:
–相關代碼定義:
適配器:(適配鵝)
public class Goose{public void hook(){System.out.println("Honk");//咯咯叫} } public class GooseApater implements Quackable{Goose goose;public GooseAdapter(Goose goose){this.goose = goose;}public void quack(){goose.honk();} } 實現:new GooseAdapter(new Goose());裝飾者:(動態添加新的行為——添加呱呱叫計數器,而不需要修改源代碼)
public class QuackCount implements Quackable{Quackable duck;static int numberOfQuacks;public QuackCounter(Quackable duck){this.duck = duck;}public void quack(){duck.quack;numberOfQuacks++;}public static int getQuacks(){return numberOfQuacks;} } 實現:new QuackCounter(new MallardDuck());抽象工廠:將創建和裝飾部分包裝起來集中管理
public abstract class AbstractDuck Factory{public abstract Quackable createMallardDuck();public abstract Quackable createRedHeadDuck();public abstract Quackable createrDuckCall();public abstract Quackable createRubberDuck(); } //創建沒有裝飾者的鴨子的工廠 public class DuckFactory extends AbstractDuckFactory{public Quackable createMallardDuck(){return new MallradDuck();}public Quackable createRedHeadDuck(){return new RedHeadDuck();}public Quackable createDuckCall(){return new DuckCall();}public Quackable createRubberDuck(){return new RubberDuck();} } //創建裝飾過的鴨子的工廠 public class CountingDuckFactory extends AbstractDuckFactory{public Quackable createMallardDuck(){return new QuackCounter(MallradDuck());}public Quackable createRedHeadDuck(){return new QuackCounter(RedHeadDuck());}public Quackable createDuckCall(){return new QuackCounter(DuckCall());}public Quackable createRubberDuck(){return new QuackCounter(RubberDuck());} }實現:AbstractDuckFactory duckFactory = new CountingDuckFactory();duckFactory.createMallardDuck();...其中比較難想到的方式代碼:
迭代器和組合模式:以一致的方式來對待鴨群和單只小鴨
public class Flock implements Quackable{ //Flock就是鴨群,處理它也可以像單只鴨子一樣調用quack方法。ArrayList quackers = new ArrayList(); //葉節點就是Quackable。public void add(Quackable quacker){quackers.add(quacker);}public void quack()//flock也是Quackable,所以也要具備quack方法{Iterator iterator = quackers.iterator();while(iterator.haxNext()){Quackable quacker = (Quackable)iterator.next();quacker.quack();}} }關于組合模式中的安全性和透明性:
觀察者模式:(追蹤某只鴨子什么時候叫)
MVC
MVC與Web
其他模式
橋接
生成器
責任鏈
蠅量
解釋器
中介者
備忘錄
原型
訪問者
完結!恭喜你閱讀完常用基礎的設計模式。
總結
以上是生活随笔為你收集整理的设计模式(注重理解为什么),的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python stdin什么意思_pyt
- 下一篇: 从王者荣耀看设计模式(虚拟代理模式)