《Head First 设计模式》(一):策略模式
1. 范例
公司做了一套鴨子模擬游戲(SimUDuck),游戲中會有各種鴨子,會游戲劃水、會呱呱叫。
2. 初始化版本—繼承
定義一個鴨子父類:Duck,并讓各種鴨子繼承此父類
鴨子父類:Duck類 方法分析:
- 所有鴨子都會呱呱叫,所以此行為由父類實現
- 所有鴨子都會游泳,所以此行為由父類實現
- 因為每一個鴨子的外觀都不盡相同,所以此外觀方法定義為抽象方法,具體內容由子類實現
鴨子父類:Duck類
package com.jbp.designpattern;/*** @ClassName: Duck* @description: 鴨子父類* @author: JiangBeiPing* @create: 2021-06-22 14:57* @Version: 1.0**/ public abstract class Duck {// 呱呱叫行為方法實現public void quack(){System.out.println("呱呱叫...");}// 游泳行為方法實現public void swim(){System.out.println("游泳...");}// 外觀:每個鴨子的外觀都不相同,由子類自己實現public abstract void display();}鴨子子類:綠頭鴨
package com.jbp.designpattern;/*** @ClassName: MallardDuck* @description: 綠頭鴨子類* @author: JiangBeiPing* @create: 2021-06-22 16:59* @Version: 1.0**/ public class MallardDuck extends Duck {@Overridepublic void display() {System.out.println("綠頭鴨...");} }鴨子子類:紅頭鴨
package com.jbp.designpattern;/*** @ClassName: RedheadDuck* @description: 紅頭鴨子類* @author: JiangBeiPing* @create: 2021-06-22 17:00* @Version: 1.0**/ public class RedheadDuck extends Duck{@Overridepublic void display() {System.out.println("紅頭鴨...");} }2.1 需求變化—新增會飛的鴨子
后續公司決定在此模擬程序新增會飛的鴨子
功能實現思路:在Duck類中加上飛行fly()方法
Duck類:
package com.jbp.designpattern;/*** @ClassName: Duck* @description: 鴨子父類* @author: JiangBeiPing* @create: 2021-06-22 14:57* @Version: 1.0**/ public abstract class Duck {// 呱呱叫行為方法實現public void quack(){System.out.println("呱呱叫...");}// 游泳行為方法實現public void swim(){System.out.println("游泳...");}// 外觀:每個鴨子的外觀都不相同,由子類自己實現public abstract void display();// 新增飛行方法public void fly(){System.out.println("飛行...");}}鴨子子類:橡皮鴨
package com.jbp.designpattern;/*** @ClassName: RubberDuck* @description: 橡皮鴨子類* @author: JiangBeiPing* @create: 2021-06-22 17:13* @Version: 1.0**/ public class RubberDuck extends Duck{// 橡皮鴨只能吱吱吱叫,所以覆蓋此方法@Overridepublic void quack() {System.out.println("吱吱吱");}@Overridepublic void display() {System.out.println("橡皮鴨...");} }帶來的問題:游戲中,所有的鴨子都會飛(橡皮、玩具鴨也都會飛)
解決方案:
把橡皮鴨子類的飛行方法fly()覆蓋成空方法。
新的問題—后面的每創建一個新的鴨子類,都需要按情況進行方法覆蓋。
package com.jbp.designpattern;/*** @ClassName: RubberDuck* @description: 橡皮鴨子類* @author: JiangBeiPing* @create: 2021-06-22 17:13* @Version: 1.0**/ public class RubberDuck extends Duck{// 橡皮鴨只能吱吱吱叫,所以覆蓋此方法@Overridepublic void quack() {System.out.println("吱吱吱");}@Overridepublic void display() {System.out.println("橡皮鴨...");}@Overridepublic void fly() {} }3. 優化版本—接口
把 fly()方法從父類 Duck類中抽取成一個 Flyable接口,只需要會飛的鴨子子類實現該接口即可,不需要飛行的鴨子子類無需理會此接口。同理,把呱呱呱叫的方法 quack() 也抽取成一個 Quackable接口。
新的問題:
代碼復用性差。
比如,有 n 個鴨子子類,就需要實現 n 次飛行或者叫聲的方法的具體內容。
4. 最終版本—策略模式
設計原則:找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼耦合在一起。
思路分析:
分開變化和不會變化的部分
變化的部分:
- quack():鴨子叫聲
- fly():飛行
不會變化的部分:
- swim():游泳
新增兩個類,一個是 fly()飛行相關的,一個是quack()鴨子叫聲相關的,每一個類實現各自的動作。
鴨子行為設計思路分析:
如何設計實現飛行和鴨子叫行為的類?
應該在鴨子類中包含設定行為的方法,這樣就可以在“運行時”動態地改變該子類的行為。比如,在產生新的綠頭鴨實例時,再指定特定的的“類型”的飛行行為給它。
設計原則:針對接口編程,而不是針對實現編程。
利用接口代表行為,比如,FlyBehavior 和 QuackBehavior,而行為的實現類自行實現自己對應的接口。
所以,鴨子子類不會去實現 Flying 和 Quacking 接口。而是由特定的行為類專門實現 FlyBehavior 和 QuackBehavior。
之前的做法是:行為來自鴨子父類Duck類,或者繼承某個行為接口,由鴨子子類自行實現。這兩種方法都是行為依賴于實現。
現在的做法是:鴨子的子類使用接口 FlyBehavior 和 QuackBehavior所表示行為,所以具體的實現由行為實現類具體實現而不是由鴨子子類實現。
代碼實現:
飛行接口:
package com.jbp.designpattern;/*** @ClassName: FlyBehavior* @description: 飛行接口:所有的飛行鴨子類都需實現此接口,并且實現fly()* @author: JiangBeiPing* @create: 2021-06-22 18:06* @Version: 1.0**/ public interface FlyBehavior {public void fly(); }飛行實現類:
package com.jbp.designpattern;/*** @ClassName: FlyWithWings* @description: 飛行行為具體實現類* @author: JiangBeiPing* @create: 2021-06-22 18:08* @Version: 1.0**/ public class FlyWithWings implements FlyBehavior {@Overridepublic void fly() {System.out.println("飛行...");} }飛行實現類:
package com.jbp.designpattern;/*** @ClassName: FlyNoWay* @description: 不會飛行的行為具體實現類* @author: JiangBeiPing* @create: 2021-06-22 18:10* @Version: 1.0**/ public class FlyNoWay implements FlyBehavior {@Overridepublic void fly() {// 什么都不做,不會飛行} }叫聲接口:
package com.jbp.designpattern;/*** @ClassName: QuackBehavior* @description: 鴨子叫聲實現接口* @author: JiangBeiPing* @create: 2021-06-22 18:14* @Version: 1.0**/ public interface QuackBehavior {public void quack();}叫聲實現類:
package com.jbp.designpattern;/*** @ClassName: Quack* @description: 鴨子呱呱呱叫行為實現* @author: JiangBeiPing* @create: 2021-06-22 18:16* @Version: 1.0**/ public class Quack implements QuackBehavior{@Overridepublic void quack() {System.out.println("呱呱叫...");} }叫聲實現類:
package com.jbp.designpattern;/*** @ClassName: Squeak* @description: 鴨子吱吱吱叫行為具體實現* @author: JiangBeiPing* @create: 2021-06-22 18:18* @Version: 1.0**/ public class Squeak implements QuackBehavior{@Overridepublic void quack() {System.out.println("吱吱吱...");} }叫聲實現類:
package com.jbp.designpattern;/*** @ClassName: MuteQuack* @description: 鴨子不能叫行為具體實現* @author: JiangBeiPing* @create: 2021-06-22 18:19* @Version: 1.0**/ public class MuteQuack implements QuackBehavior{@Overridepublic void quack() {// 什么都不做,不會叫} }4.1 修改鴨子父類 Duck 類
現在鴨子的飛行和叫聲動作會由行為類進行具體實現,而不是在Duck 類或子類中自己實現。
首先,在Duck 類中新增兩個實例變量,分別是 FlyBehavior 和 QuackBehavior ,聲明為接口類型,而不是具體類實現類型。每個鴨子子類對象都會動地設置這些變量以在運行時引用正確的行為類型。
然后,用兩個方法 performFly() 和 performQuack()替換Duck 類中的 fly()和quack()。
Duck 類:
package com.jbp.designpattern;/*** @ClassName: Duck* @description: 鴨子父類* @author: JiangBeiPing* @create: 2021-06-22 14:57* @Version: 1.0**/ public abstract class Duck {// 每個鴨子子類都會引用實現FlyBehavior接口的對象FlyBehavior flyBehavior;QuackBehavior quackBehavior;// 游泳行為方法實現public void swim(){System.out.println("游泳...");}// 外觀:每個鴨子的外觀都不相同,由子類自己實現public abstract void display();// 鴨子對象不親自處理叫聲行為,而是由quackBehavior引用的對象實現public void performQuack(){quackBehavior.quack();}public void performFly(){flyBehavior.fly();}}綠頭鴨子類:
package com.jbp.designpattern;/*** @ClassName: MallardDuck* @description: 綠頭鴨子類* @author: JiangBeiPing* @create: 2021-06-22 16:59* @Version: 1.0**/ public class MallardDuck extends Duck {@Overridepublic void display() {System.out.println("綠頭鴨...");}// 當綠頭鴨子類實例化時,構造器會把繼承來的quackBehavior實例變量初始化成Quack類的新實例public MallardDuck(){// 綠頭鴨使用Quack類處理叫聲進行呱呱叫quackBehavior = new Quack();flyBehavior = new FlyWithWings();} }測試:
4.2 動態設定行為(避免構造器中設定)
在鴨子子類中通過屬性設置方法(setter method)來設置鴨子的行為,從而避免在鴨子的構造器內實例化。
Duck 類新增setter方法:
package com.jbp.designpattern;/*** @ClassName: Duck* @description: 鴨子父類* @author: JiangBeiPing* @create: 2021-06-22 14:57* @Version: 1.0**/ public abstract class Duck {// 每個鴨子子類都會引用實現FlyBehavior接口的對象FlyBehavior flyBehavior;QuackBehavior quackBehavior;// 游泳行為方法實現public void swim(){System.out.println("游泳...");}// 外觀:每個鴨子的外觀都不相同,由子類自己實現public abstract void display();// 鴨子對象不親自處理叫聲行為,而是由quackBehavior引用的對象實現public void performQuack(){quackBehavior.quack();}public void performFly(){flyBehavior.fly();}public void setFlyBehavior(FlyBehavior flyBehavior) {this.flyBehavior = flyBehavior;}public void setQuackBehavior(QuackBehavior quackBehavior) {this.quackBehavior = quackBehavior;} }鴨子子類:模型鴨:
package com.jbp.designpattern;/*** @ClassName: ModelDuck* @description: 模型鴨子類* @author: JiangBeiPing* @create: 2021-06-23 11:02* @Version: 1.0**/ public class ModelDuck extends Duck{@Overridepublic void display() {System.out.println("模型鴨...");}public ModelDuck() {// 模型鴨不會飛flyBehavior = new FlyNoWay();quackBehavior = new Quack();} }新增新的飛行行為:火箭飛行:
package com.jbp.designpattern;/*** @ClassName: FlyRocketPowered* @description: 火箭動力飛行* @author: JiangBeiPing* @create: 2021-06-23 11:05* @Version: 1.0**/ public class FlyRocketPowered implements FlyBehavior{@Overridepublic void fly() {System.out.println("火箭動力飛行...");} }測試:
package com.jbp.designpattern;/*** @ClassName: DuckTest* @description: 鴨子類測試* @author: JiangBeiPing* @create: 2021-06-22 17:16* @Version: 1.0**/ public class DuckTest {public static void main(String[] args) {Duck modelDuck = new ModelDuck();// 第一次調用performFly(),飛行行為由FlyBehavior接口(具體實現由FlyNoWay實現),在模型鴨構造器設置modelDuck.performFly();// 調用父類的setter方法,把火箭飛行行為設置到模型鴨中modelDuck.setFlyBehavior(new FlyRocketPowered());// 模型鴨動態地改變自身的飛行行為modelDuck.performFly();} }5. 總結
重構后,類分為:鴨子子類繼承Duck、飛行行為實現FlyBehavior接口、呱呱叫行為實現QuackBehavior接口。
描述事情的方式也從“一組行為”改成“一族算法”,算法表示鴨子能做的事情(不同的叫聲和不同的飛行方式)。
每一個鴨子都有一個FlyBehavior和QuackBehavior,飛行和叫聲行為都由其行為具體實現類實現。當兩個類結合起來一起使用,如同測試類中,即是組合(composition)。這種方式和繼承的區別是,鴨子子類的行為不是繼承而來的,而是和適當的行為對象組合而來。
設計原則:多用組合,少用繼承
策略模式:定義了算法族,分別封裝起來,讓他們之間可以互相替換,此模式讓算法的變化獨立于使用算法的客戶。
總結
以上是生活随笔為你收集整理的《Head First 设计模式》(一):策略模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: platform.pk8,platfor
- 下一篇: HeadFirst设计模式学习笔记