[DesignPatterns] 以JAVA為範例,介紹策略模式(Strategy Pattern)

[DesignPatterns] 以JAVA為範例,介紹策略模式(Strategy Pattern)

 

前言 

最近在看設計模式相關的書籍,想要在這裡跟大家分享一下設計模式,一邊整理想法,一邊想一下那些情境適合應用。小弟新手一枚如果有錯誤,歡迎指教。  

 

目錄 

  • 甚麼是策略模式 

  • 使用情境 

  • 範例 

  • Reference

 

甚麼是策略模式 

在不同的場景中應該要有不一樣的方法。 

 

使用情境 

像是一間外商公司,他在面對經銷商跟面對客戶,計算商品的價錢算法可能就不一樣,而我們把這些演算價錢的方法分出來,讓同樣是經銷商的人有一樣的算法,讓一般客戶是一樣的算法。 

 

範例

以情境來寫範例吧。 

  1. 讓我們假想一下,如果我們是外商公司,一開始我們只對A客戶跟B客戶提供銷售服務,而在銷售後有直接的現金優惠直覺的我們可能會直接寫一個類別(Customer) 讓A客戶跟B客戶都繼承 

 


    public class Customer {
        private String name ;
        public Customer(String name){
            this.name = name;
        }

        public void discount() {
            System.out.println("Money Discount");
        }
    }

    public class ACustomer extends Customer {
        public ACustomer(){
            super("ACustomer");
        }
    }

    public class BCustomer extends Customer {
        public BCustomer(){
            super("BCustomer");
        }
    }

  1. 後來公司規模越來越大,我們不想要直接提供現金折扣給新的客戶了,但是對A、B客戶還是一樣有提供,畢竟他們是老客戶了,但是我們改提供累積紅利換公司商品的優惠,現在有一個新客戶C,C客戶是新加入的,也就適用於累積紅利的方案。那我們應該要怎麼改寫這段程式呢? 

你可能直覺的就直接寫了一個CCustomer,直接覆寫了Discount(),形成下面這樣,也解決了問題。

    public class CCustomer extends Customer{
        public CCustomer(){
            super("CCustomer");
        }

        @Override
        public void discount(){
            System.out.println("Bonus Point Discount");
        }
    }

 

  1. 當我們改完沒多久,這時候又一個客戶D要新加入,當然客戶D也是用紅利折扣,這時候怎麼辦?? 我們要再寫一個Discount來複寫嗎?? QQ

這時候這就可利用到策略模式的特性,把演算法(範例指的是Discount 這個方法)的部分抽出來。那我們可以怎麼改寫呢? 

我們把折扣定義成一個介面,把不同的折扣方式實作起來,像這樣


    public interface Discountable{
        void discount();
    }
    
    public class MomenyDiscount implements Discountable{
        @Override
        public void discount() {
            System.out.println("Money Discount");
        }
    }
    
    public class BonusPointDiscount implements Discountable{
        @Override
        public void discount() {
            System.out.println("Bonus Point Discount");
        }
    }

這樣做的好處是當我們在使用Customer這個類別時,可以直接有一個物件叫做discountable,讓這個去只到我們實作的方法就可以了,效果是不是很明顯就可以感受到了


    public class Customer {
        private String name ;
        private Discountable discountable;

        public Customer(String name, Discountable discountable){
            this.name = name;
            this.discountable = discountable;
        }

        public void discount() {
            discountable.discount();
        }
    }

    public class ACustomer extends Customer {
        public ACustomer(){
            super("ACustomer", new MomenyDiscount());
        }
    }

    public class DCustomer extends Customer{
        public DCustomer(){
            super("DCustomer", new BonusPointDiscount());
        }
    }

當我們在使用的時候就只要ACustomer.discount(); 就可以達到不同的效果。

這樣做的彈性也很大,當我們突然想改變折扣方式,讓C也可以現金折扣,在Customer裡面新增一個setDiscountable 的方法,把新的方法放進去,我們就可以達到目的了

    
    public void showCase(){
        Customer customerC = new CCustomer();
        customerC.discount();
        customerC.setDiscountable(new MomenyDiscount());
        customerC.discount();
    }

    public class Customer {
        private String name ;
        private Discountable discountable;

        public Customer(String name, Discountable discountable){
            this.name = name;
            this.discountable = discountable;
        }

        public void setDiscountable(Discountable discountable) {
            this.discountable = discountable;
        }

        public void discount() {
            discountable.discount();
        }
    }

這樣我們就不怕客戶再多了,是不是很方便維護阿,歡迎大家討論一下,有寫錯的地方請見諒,謝謝你看完這篇

 

 

Reference

Head First Design Patterens - O'Reilly

 

Sample Code

StrategyPattern