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