設(shè)計(jì)模式之 ———策略模式


  • 設(shè)計(jì)模式概述
    • 定義
      設(shè)計(jì)模式是一套被反復(fù)使用,多數(shù)人知曉的,經(jīng)過(guò)分類(lèi)的,代碼設(shè)計(jì)經(jīng)驗(yàn)總結(jié)

      簡(jiǎn)單來(lái)說(shuō):經(jīng)驗(yàn)復(fù)用!

    • 使用語(yǔ)言:

      并不要求一定用某種語(yǔ)言,理論上適合于任何oo語(yǔ)言(面向?qū)ο缶幊陶Z(yǔ)言)(Java,C#等)

    • 意義&目的:

      代碼復(fù)用,增加可維護(hù)性。每種模式在現(xiàn)時(shí)中都有相應(yīng)的原理來(lái)與之對(duì)應(yīng),每一個(gè)模式描述了一個(gè)在我們周?chē)粩嘀貜?fù)發(fā)生的問(wèn)題,以及該問(wèn)題的核心解決方案,這也是它能被廣泛應(yīng)用的原因。


  • 策略模式概述
    • 預(yù)備知識(shí):本文以java舉例,請(qǐng)先了解 類(lèi)、復(fù)用、繼承、多態(tài)、接口,抽象類(lèi)等
    • 策略模式( Strategy Pattern):定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換。本模式使得算法的變化可獨(dú)立于使用它的客戶(hù)。
    • (劃重點(diǎn)!!!)最主要用法:許多相關(guān)的類(lèi)僅僅是行為有異。“策略”提供了一種用多個(gè)行為中的一個(gè)行為來(lái)配置一個(gè)類(lèi)的方法

正文!

用例子理解:

有一個(gè)Duck()類(lèi)

鴨子會(huì)叫

所以有一個(gè)Quack()方法

因?yàn)榧t頭鴨和綠頭鴨會(huì)飛,現(xiàn)在,要新加入一種 fly()方法。

方法一:繼承

繼承是指一個(gè)對(duì)象直接使用另一對(duì)象的屬性和方法。

即直接在Duck類(lèi)里面加入fly()方法,再由子類(lèi)繼承。

但是!!!

不是所有的鴨子都會(huì)飛

(橡皮鴨問(wèn)號(hào)???)

那么,對(duì)于每一個(gè)Duck子類(lèi)的實(shí)例對(duì)象,都必須覆蓋fly()方法(會(huì)飛的不會(huì)飛的,乃至用不同姿態(tài)飛的鴨子)

然而,如果還有一個(gè)誘餌鴨:

不會(huì)飛也不會(huì)叫。。。。。。

那么,又得覆蓋Quack()方法。

代碼:

public class StrategyModel1 {
    public static void main(String[] args) {
        Duck1 duck=new Duck1();
        System.out.println("Duck1:");
        duck.fly1();
        duck.Quack1();
        duck.Swim1();
        Duck1 rubberduck=new rubberDuck1();
        System.out.println("RubberDuck1:");
        rubberduck.fly1();
        rubberduck.Quack1();
        rubberduck.Swim1();
        Duck1 Decoyduck=new DecoyDuck1();
        System.out.println("DecoyDuck1:");
        Decoyduck.fly1();
        Decoyduck.Quack1();
        Decoyduck.Swim1();
    }
}
class Duck1{
     public void fly1() {
         System.out.println("I'm flying!");
    }
     public void Quack1() {
         System.out.println("呱呱呱");
     }
     public void Swim1() {
         System.out.println("All the Ducks are swimming!");
     }
}
class rubberDuck1 extends Duck1{
    public void fly1() {
        System.out.println("I can't fly!");//point1
    }
    public void Quack1() {
        System.out.println("吱吱吱");
    }
}
/*看似可行,但是可能不同的類(lèi)有相同的方法,直接繼承會(huì)繼承不相同不想要的方法。

比如DecoyDuck1想要用RubberDuck1的fly1方法,要么繼承Duck1,再重寫(xiě)fly1方法;

要么不繼承Duck1而繼承RubberDuck1,再重寫(xiě)Quack1方法。

無(wú)論哪種繼承,都會(huì)導(dǎo)致代碼在多個(gè)子類(lèi)中重復(fù)。*/
class DecoyDuck1 extends Duck1{
    public void fly1() {
        System.out.println("I can't fly!");//point2
    }
    public void Quack1() {
        System.out.println("呱呱呱");
    }
}

結(jié)論:

1.利用繼承的方法,改動(dòng)會(huì)牽一發(fā)而動(dòng)全身,造成其他不想要的改變

2.代碼在多個(gè)子類(lèi)中重復(fù)。

簡(jiǎn)單來(lái)說(shuō):有新方法子類(lèi)就要覆蓋父類(lèi)。覆蓋其實(shí)沒(méi)有邏輯問(wèn)題,問(wèn)題是同樣的方法在不同類(lèi)里面要重寫(xiě)多次,代碼繁瑣。

方法二:接口

繼承不行。因?yàn)轼喿訒?huì)不斷更新,這樣每當(dāng)有新的鴨子類(lèi)出現(xiàn),就得檢查甚至覆蓋fly()和Quack()方法。

那么
我們可以把fly()從父類(lèi)中取出來(lái),放到Flyalbe接口中,這樣,只有會(huì)飛的鴨子才能實(shí)現(xiàn)此接口。Quackable接口同理。

接口:(interface),是一系列方法的聲明,是一些方法特征的集合,一個(gè)接口只有方法的特征沒(méi)有方法的實(shí)現(xiàn),因此這些方法可以在不同的地方被不同的類(lèi)實(shí)現(xiàn),而這些實(shí)現(xiàn)可以具有不同的行為(功能)。

【注意與抽象類(lèi)區(qū)分!】(抽象類(lèi)是對(duì)一種事物的抽象,即對(duì)類(lèi)抽象{is-a}(全部方法和屬性繼承)。而接口是對(duì)行為的抽象{has-a}。(指定方法繼承))

代碼:

public class StrategyModel2 {
    public static void main(String[] args) {
        Duck2 duck=new Duck2();
        duck.fly2();
        Duck2 rubberduck=new rubberDuck2();
        rubberduck.fly2();
    }
}
class rubberDuck2 extends Duck2 implements Flyable2 {
    public void fly2() {
        System.out.println("I don't wanna to fly!");
    }
}
class Duck2 implements Flyable2{
    public void fly2() {
        System.out.println("I'm flying!");
    }
}
interface Flyable2{
    public abstract void fly2();
}
//Flyalbe2導(dǎo)致fly2完全不能復(fù)用

現(xiàn)在雖然Flyable接口可以解決會(huì)飛的橡皮鴨問(wèn)題,

但是!!!

這是一種更蠢的方法,只有方法聲明,沒(méi)有方法實(shí)現(xiàn)!

代碼將完全無(wú)法復(fù)用,無(wú)論方法是否一樣,每次必須重寫(xiě)。

方法三:策略模式!

繼承不能解決問(wèn)題,因?yàn)轼喿拥男袨樵谧宇?lèi)里不斷改變,而且讓所有子類(lèi)都有這些行為是不恰當(dāng)?shù)摹?/p>

接口似乎可行,只有會(huì)飛的鴨子繼承Flyable接口,但是Java接口不具有實(shí)現(xiàn)代碼,繼承接口無(wú)法使代碼復(fù)用。

有一個(gè)設(shè)計(jì)原則,恰好適用于此:(第一個(gè)設(shè)計(jì)原則!)

找出變化之處,把它獨(dú)立出來(lái),與無(wú)需變化的代碼隔離

這個(gè)概念很簡(jiǎn)單,

幾乎是每個(gè)設(shè)計(jì)模式背后的精神所在!

哪些可變哪些不可變?

每次新的需求一來(lái),都會(huì)使某方面的代碼發(fā)生變化,那么這部分代碼就要被抽出來(lái),區(qū)分與其他穩(wěn)定代碼。

Fly()和Quack()會(huì)隨著鴨子的類(lèi)不同而改變。
我們把它們從Duck類(lèi)中取出,新建一組類(lèi)來(lái)代表每個(gè)行為

第二個(gè)設(shè)計(jì)原則:針對(duì)接口編程!而不是針對(duì)實(shí)現(xiàn)編程

我們利用接口代表每個(gè)行為,比方說(shuō),F(xiàn)lyBehavior,而行為的每個(gè)實(shí)現(xiàn)都將實(shí)現(xiàn)其中的一個(gè)接口。

這樣的做法迥異于以往,以前的做法是:行為來(lái)自Duck父類(lèi)的具體實(shí)現(xiàn),或是繼承某個(gè)接口并由子類(lèi)自行實(shí)現(xiàn)而來(lái)。這兩種做法都是依賴(lài)于“實(shí)現(xiàn)”,我們被實(shí)現(xiàn)綁得死死的,沒(méi)辦法更改行為(除非寫(xiě)更多代碼)。

在我們的新設(shè)計(jì)中,鴨子的子類(lèi)將使用接口FlyBehavior所表示的行為,所以實(shí)際的“實(shí)現(xiàn)”不會(huì)被綁死在鴨子的子類(lèi)中。

換句話(huà)說(shuō),特定的具體行為編寫(xiě)在實(shí)現(xiàn)了FlyBehavior的類(lèi)中

這樣設(shè)計(jì),甚至可以讓fly被其他對(duì)象復(fù)用,因?yàn)檫@個(gè)fly動(dòng)作已經(jīng)與鴨子類(lèi)無(wú)關(guān)了。

代碼:

public class StrategyModel3 {
    public static void main(String[] args) {
        Duck3 duck=new Duck3();
        duck.Swim3();
        Duck3 rubberduck=new RubberDuck3();
        rubberduck.Swim3();
        rubberduck.PerformFly();
        Duck3 decoyduck=new DecoyDuck3();
        decoyduck.PerformFly();
    }
}
class Duck3{
    public FlyBehavior flybehavior;
    public void Swim3() {
        System.out.println("All the ducks are swimming!");
    }
    public void PerformFly() {
        flybehavior.fly3();
    }
}
class RubberDuck3 extends Duck3{
    public RubberDuck3() {
    flybehavior=new FlyWithWings();
    }
}
class DecoyDuck3 extends Duck3{
    public DecoyDuck3() {
        flybehavior=new FlyNoWay();
    }
}
interface FlyBehavior{
    public void fly3();
}
class FlyWithWings implements FlyBehavior{
    public void fly3() {
        System.out.println("I'm flying!");
    }
}
class FlyNoWay implements FlyBehavior{
    public void fly3() {
        System.out.println("I can't fly!");
    }
}

(Quack同理)

將兩個(gè)類(lèi)結(jié)合起來(lái)使用,如同本例,就是組合。這種做法和“繼承”不同的地方在于,鴨子的行為不是繼承來(lái)的,而是和適當(dāng)?shù)男袨閷?duì)象組合來(lái)的。

第三個(gè)設(shè)計(jì)原則:多用組合,少用繼承!


復(fù)習(xí):

策略模式:定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái), 并且使它們可相互替換。本模式使得算法的變化可獨(dú)立于使用它的客戶(hù)。

概念圖示:


結(jié)束
------By@YYSir

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容