《HeadFirst設計模式》讀書筆記一:策略模式

做者:摺紙
我的博客: https://www.zhezhi.press
記錄菜雞的成長之旅!

1 從一堆鴨子提及

衆所周知《機器學習》(周志華版)被稱爲西瓜書,我以爲《Head First 設計模式》也能夠被稱爲鴨子書~ 假設有這麼一個模擬鴨子游戲SimUDuck,遊戲中會出現各類鴨子,它們一邊游泳一邊呱呱叫。因而有這麼一個鴨子超類,而且讓各類鴨子繼承這個超類;算法

image.png
這看起來彷佛還不錯,全部的鴨子都會繼承Duck超類,而且只須要重寫display方法以打印本身的特徵就能夠。但需求是在變化的編程

2 讓鴨子飛起來!

好吧,看起來不難,咱們只須要給Duck類加一個fly()方法並實現就能夠了;確實這樣一來全部繼承Duck類的鴨子都會飛了,可是咱們忽略了一個事實——不一樣子類之間的差別性,橡皮鴨子本來不會飛如今飛起來了!設計模式

對代碼的局部修改 影響的不必定只停留在局部;當涉及「維護」時,爲了「複用」目的而使用繼承,結局並不完美
個人理解: 對超類的修改要謹慎,避免形成沒必要要的麻煩!如無必要,勿增實體

2.1 解決辦法:子類重寫

咱們能夠重寫不會飛的橡皮鴨子的fly方法,將其改成什麼也不作就能夠了。
這確實解決了橡皮鴨能夠飛的問題,可是之後每次設計新的子類的時候,咱們都須要檢查fly、quack、swim等方法,這就失去了複用的意義。機器學習

經過繼承提供Duck的行爲,可能會帶來的缺點學習

代碼在多個子類中重複(須要改寫行爲以適應不一樣子類)
運行時的行爲不容易改變(沒法靈活切換)
很難知道全部鴨子的所有行爲
改變會牽一髮而動全身,致使其餘鴨子不想要的改變

2.2 解決辦法:經過接口

接口是對行爲的抽象,能夠把fly()、quack()從超類中提取出來,放進Flyable、Quackable接口,而後只有會飛、會叫的鴨子纔回去實現這個接口。
但這一來重複的代碼將變得更多,對於每一種會飛、會叫的鴨子都須要去實現對應的接口,此外還有不一樣飛行方法、不一樣叫聲的叫法,這使得代碼的可複用性降得很低。spa

2.3 從新審視問題

首先咱們知道在這個案例中,fly是新加入的需求~
而且使用繼承並不能很好地解決問題,由於鴨子的行爲在子類裏不斷地改變,而且讓全部的子類都有這些行爲是不恰當的。Flyable與Quackable接口一開始彷佛還挺不錯,解決了問題(只有會飛的鴨子才繼承Flyable),可是Java接口不具備實現代碼,因此繼承接口沒法達到代碼的複用。
這意味着: 不管什麼時候你須要修改某個行爲,你必須得往下追蹤並在每個定義此行爲的類中修改它,一不當心,可能會形成新的錯誤!
幸運的是,有一個設計原則,剛好適用於此情況。設計

設計原則: 找出應用中可能須要變化之處,把它們獨立出來,不要和那些不須要變化的代碼混在一塊兒。

下面是這個原則的另外一種思考方式:「把會變化的部分取出並封裝起來,以便之後能夠輕易地改動或擴充此部分,而不影響不須要變化的其餘部分」。-->系統變得更有彈性code

這樣的概念很簡單,幾乎是每一個設計模式背後的精神所在。全部的模式都提供了一套方法讓「系統中的某部分改變不會影響其餘部分」。
好,該是把鴨子的行爲從Duck類中取出的時候了!對象

3 抽離易於變化的部分

一種方法是創建2組徹底遠離Duck類的Class,每一組類將實現fly、或者是quack,而類中有對於fly的不一樣實現;並且可使得鴨子的行爲能夠動態地改變(setter)繼承

設計原則:針對接口編程,而不是針對實現編程。

咱們利用接口表明每一個行爲,行爲的每一個實現都將實現其中的一個接口。這樣的作法迥異於以往,之前的作法是:行爲來自Duck超類的具體實現,或是繼承某個接口並由子類自行實現而來。

這兩種作法都是依賴於「實現」,咱們被實現綁得死死的,沒辦法更改行爲(除非寫更多代碼)。在咱們的新設計中,鴨子的子類將使用接口(FlyBehavior與QuackBehavior)所表示的行爲,因此實際的「實現」不會被綁死在鴨子的子類中(鴨子不會負責實現Flyable、Quackable接口)。(換句話說,特定的具體行爲編寫在實現了FlyBehavior與QuakcBehavior的類中,從而實現瞭解耦)。

這樣的設計,可讓飛行和呱呱叫的動做被其餘的對象複用,由於這些行爲已經與鴨子類無關了;而且咱們也能夠新增一些行爲(不一樣的實現),不會影響到已有的行爲類和「使用」其餘已有行爲的鴨子類。

這麼一來,有了繼承的「複用」好處,卻沒有繼承所帶來的包袱。

image.png

4 覆盤

你也許可能聽過Has-A is Better than is-A的說法,也許你可能像我同樣此前並無很直接地感覺到或者思考過Why Has-A is Better than is-A?
但經過鴨子的例子我想你應該有一些感覺了,is-A也就是繼承的關係(不一樣的鴨子繼承超類Duck),而has-A更偏向於組合的關係(鴨子類有FlyBehavior類和QuackBehavior類,鴨子是容器/對象,然後二者是容器/對象的組件類)

 設計原則:多用組合,少用繼承

如你所見,使用組合創建系統具備很大的彈性,不只可將算法族(對於同一個行爲的不一樣實現方法)封裝成類,更能夠「在運行時動態地改變行爲」(setter方法),只要組合的行爲對象符合正確的接口標準便可。
組合用在許多設計模式中,它也有許多優勢和缺點,在以後的學習中咱們還會繼續討論;

5 策略模式

策略模式是一種行爲型設計模式, 它能夠在運行時選擇算法;與直接實現某個算法不一樣,代碼在運行時能夠選擇在算法族中選取一個執行。
(In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.wikipedia)

或者說

策略模式將某一個行爲(接口)經過不一樣的方法/算法實現[上文所提的算法族],方便須要實現該接口對應行爲[ Has-A]的程序/客戶能夠在"運行時" 靈活切換該行爲實現的方法/策略,使得策略的變化獨立於使用它的客戶/程序;
相關文章
相關標籤/搜索