設計模式(二十三)——策略模式(Arrays原始碼分析)

十四lin發表於2021-02-12

1 編寫鴨子專案,具體要求如下:

1) 有各種鴨子(比如 野鴨、北京鴨、水鴨等, 鴨子有各種行為,比如 叫、飛行等)

2) 顯示鴨子的資訊

2 傳統方案解決鴨子問題的分析和程式碼實現

1) 傳統的設計方案(類圖)

 

 

 2)程式碼實現

package com.lin.strategy;

public abstract class Duck {

    public abstract void display();
    
    public void quack() {
        System.out.println("鴨子嘎嘎嘎嘎");
    }
    
    public void swimming() {
        System.out.println("鴨子會游泳");
    }
    
    public void fly() {
        System.out.println("鴨子會飛");
    }
}

 

package com.lin.strategy;

public class PekingDuck extends Duck {

    @Override
    public void display() {
        System.out.println("這是北京鴨");

    }
    
    // 北京鴨不好飛翔
    @Override
    public void fly() {
        System.out.println("北京鴨不會飛翔");
    }

}

 

package com.lin.strategy;

public class ToyDuck extends Duck {

    @Override
    public void display() {
        System.out.println("玩具鴨");

    }
    
    // 要重寫所有父類的方法
    public void quack() {
        System.out.println("鴨子不會嘎嘎嘎嘎");
    }
    
    public void swimming() {
        System.out.println("鴨子不會游泳");
    }
    
    public void fly() {
        System.out.println("鴨子不會飛");
    }

}

 

package com.lin.strategy;

public class WildDuck extends Duck{

    @Override
    public void display() {
        System.out.println("這是野鴨!");        
    }

}

3 傳統的方式實現的問題分析和解決方案

1) 其它鴨子,都繼承了 Duck 類,所以 fly 讓所有子類都會飛了,這是不正確的

2) 上面說的 1 的問題,其實是繼承帶來的問題:對類的區域性改動,尤其超類的區域性改動,會影響其他部分。會有溢位效應

3) 為了改進 1 問題,我們可以通過覆蓋 fly  方法來解決 => 覆蓋解決

4) 問題又來了,如果我們有一個玩具鴨子 ToyDuck, 這樣就需要 ToyDuck 去覆蓋 Duck 的所有實現的方法 => 解決思路 - 策略模式 (strategy pattern)

4 策略模式基本介紹

1) 策略模式(Strategy Pattern)中,定義演算法族(策略組),分別封裝起來,讓他們之間可以互相替換,此模式讓演算法的變化獨立於使用演算法的客戶

2) 這演算法體現了幾個設計原則,第一、把變化的程式碼從不變的程式碼中分離出來;第二、針對介面程式設計而不是具體類(定義了策略介面);第三、多用組合/聚合,少用繼承(客戶通過組合方式使用策略)。

5 策略模式的原理類圖

 

 

 說明:從上圖可以看到,客戶 context 有成員變數 strategy 或者其他的策略介面

,至於需要使用到哪個策略,我們可以在構造器中指定

策略模式解決鴨子問題

1) 應用例項要求

編寫程式完成前面的鴨子專案,要求使用策略模式

2) 思路分析(類圖)

策略模式:分別封裝行為介面,實現演算法族,超類裡放行為介面物件,在子類裡具體設定行為物件。原則就是: 分離變化部分,封裝介面,基於介面程式設計各種功能。此模式讓行為的變化獨立於演算法的使用者

 

 

 

 3)程式碼實現

package com.lin.strategy.plus;

public abstract class Duck {

    // 策略介面
    public FlyBehavior flyBehavior;
    
    public abstract void display();
    
    public void quack() {
        System.out.println("鴨子嘎嘎嘎嘎");
    }
    
    public void swimming() {
        System.out.println("鴨子會游泳");
    }
    
    public void fly() {
        if(flyBehavior != null) {
            flyBehavior.fly();
        }
    }
    
    // 動態改變某個物件的行為
    public void setFly(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
}

 

package com.lin.strategy.plus;

public class WildDuck extends Duck{

    public WildDuck() {
        super.flyBehavior = new GoodFly();
    }
    
    @Override
    public void display() {
        System.out.println("這是野鴨!");        
    }

}

 

package com.lin.strategy.plus;

public class ToyDuck extends Duck {

    public ToyDuck() {
        flyBehavior = new NotFly();
    }
    
    @Override
    public void display() {
        System.out.println("玩具鴨");

    }
    
    // 要重寫所有父類的方法
    public void quack() {
        System.out.println("鴨子不會嘎嘎嘎嘎");
    }
    
    public void swimming() {
        System.out.println("鴨子不會游泳");
    }
    
    

}

 

package com.lin.strategy.plus;

public class PekingDuck extends Duck {

    public PekingDuck() {
        flyBehavior = new NotFly();
    }
    
    @Override
    public void display() {
        System.out.println("北京鴨!");

    }
    
    
    

}

 

package com.lin.strategy.plus;

public class GoodFly implements FlyBehavior{

    @Override
    public void fly() {
        System.out.println("飛翔技術十分好");
    }

}

class NotFly implements FlyBehavior{

    @Override
    public void fly() {
        System.out.println("不會飛翔");
    }

}

class BadFly implements FlyBehavior{

    @Override
    public void fly() {
        System.out.println("飛翔技術很差");
    }

}

 

package com.lin.strategy.plus;

public interface FlyBehavior {

    void fly();
}

 

package com.lin.strategy.plus;

public class Client {

    public static void main(String[] args) {
        
        PekingDuck pekingDuck = new PekingDuck();
        pekingDuck.fly();
        
        // 動態改變某個物件的行為
        pekingDuck.setFly(new GoodFly());
        pekingDuck.fly();
    }
    
}

策略模式在 JDK-Arrays 應用的原始碼分析

1) JDK  Arrays  Comparator 就使用了策略模式

2)說明:從上圖可以看到,客戶 context 有成員變數 strategy 或者其他的策略介面

3)程式碼分析+模式角色分析

 

 

 

package com.lin.strategy.plus;

import java.util.Arrays;
import java.util.Comparator;

public class StrategyTest {

    public static void main(String[] args) {
        
        // 實現降序排序,返回-1 放左邊,1 放右邊,0 保持不變
        // 說 明
        // 1.  實現了 Comparator 介面(策略介面) , 匿名類 物件 new Comparator<Integer>(){..}
        // 2.  物件 new Comparator<Integer>(){..}  就是實現了 策略介面 的物件
        // 3. public int compare(Integer o1, Integer o2){} 指定具體的處理方式
        Integer[] data = {3,4,6,78,1,0,-91};
        Comparator<Integer> comparator = new Comparator<Integer>() {

            @Override
            public int compare(Integer o1, Integer o2) {
                if(o1<o2) {                // 降序,升序
                    return 1;
                } else {
                    return -1;
                }
            }
        };
        
        // 方式一
        Arrays.sort(data, comparator);
        System.out.println(Arrays.toString(data));
        
        // 方式二
        Integer[] data1 = {3,4,6,78,1,0,-91};
        Arrays.sort(data1, (var1, var2) -> { 
            if(var1.compareTo(var2) > 0) {
                return 1;
            } else {
                return -1;
            }
        });
        
        System.out.println(Arrays.toString(data1));
    }
}

策略模式的注意事項和細節

1) 策略模式的關鍵是:分析專案中變化部分與不變部分

2) 策略模式的核心思想是:多用組合/聚合 少用繼承;用行為類組合,而不是行為的繼承。更有彈性

3) 體現了“對修改關閉,對擴充套件開放”原則,客戶端增加行為不用修改原有程式碼,只要新增一種策略(或者行為) 即可,避免了使用多重轉移語句(if..else if..else

4) 提供了可以替換繼承關係的辦法: 策略模式將演算法封裝在獨立的 Strategy 類中使得你可以獨立於其 Context 改變它,使它易於切換、易於理解、易於擴充套件

5) 需要注意的是:每新增一個策略就要增加一個類,當策略過多是會導致類數目龐,至於需要使用到哪個策略,我們可以在構造器中指定

 

 

僅供參考,有錯誤還請指出!

有什麼想法,評論區留言,互相指教指教。 

覺得不錯的可以點一下右邊的推薦喲!

祝大家牛年大吉大利,牛氣沖天!

 

相關文章