1. 程式人生 > 其它 >javaweb學習28:郵件傳送原理及實現

javaweb學習28:郵件傳送原理及實現

策略模式屬於行為型模式,是使用最多的設計模式之一;其作用是針對一組演算法,將每一個演算法封裝到具體共同介面的獨立的類種,從而使得他們可以相互轉化。策略模式使得演算法可以在不影響到客戶端得情況下發生變化。

在開發過程中,經常會遇到某種業務存在多種策略可供選擇的情況,比如超市促銷打折,旅遊出行方案等。對於這一類的業務,按照傳統的方法,主要有以下兩種方案:

  1. 所有的業務邏輯都放在客戶端裡面。客戶端利用條件選擇語句決定使用哪一個演算法。這樣一來,客戶端程式碼會變得複雜和難以維護。
  2. 客戶端可以利用繼承的辦法在子類裡面實現不同的行為,但是這樣會使得環境和行為緊密耦合在一起。強耦合會使兩者不能單獨演化。

其實使用策略模式

正是解決這個問題的系統化方法。當出現新的業務策略時,只需要實現新的策略類,並在客戶端登記即可。策略模式相當於“可插入式(Pluggable)的演算法”。策略模式就是把行為和環境分割開來。環境類負責維持和查詢行為類,各種演算法則在具體策略類(ConcreteStrategy)中提供。由於演算法和環境獨立開來,演算法的增減、修改都不會影響環境和客戶端。

當準備在一個系統裡使用策略模式時,首先必須找到需要包裝的演算法,看看演算法是否可以從環境中分割開來,最後再考察這些演算法是否會在以後發生變化。

策略模式的UML類圖如下:

由上圖可以看出,策略模式主要涉及到抽象策略角色、具體策略角色、環境角色三種角色:

  • 抽象策略角色:通常由一個介面或抽象類實現,次角色給出所有的具體策略類所需的介面
  • 具體策略角色:包裝了相關的演算法或行為
  • 環境角色:持有一個策略類的引用

圖書折扣的例子

在一個購買圖書的系統中,主要由一些幾種不同的折扣:

折扣一(NoDiscountStrategy):對有些圖書沒有折扣。折扣演算法物件返還0作為折扣值。

折扣二(FlatRateStrategy):對有些圖書提供一個固定量值為1元的折扣。

折扣三(PercentageStrategy):對有些圖書提供一個百分比的折扣,比如本書價格為 20元,折扣百分比為7%,那麼折扣值就是20×7%=1.4(元)。

例子的UML類圖:

抽象策略角色:

package com.charon.strategy;

/**
 * @className: DiscountStrategy
 * @description:
 * @author: charon
 * @create: 2022-04-10 11:48
 */
public abstract class DiscountStrategy {

    /**
     * 價格
     */
    private double price = 0;

    /**
     * 數量
     */
    private int copies;

    public DiscountStrategy() {
    }

    public DiscountStrategy(double price, int copies) {
        this.price = price;
        this.copies = copies;
    }

    /**
     * 計算價格的策略方法
     * @return
     */
    abstract double calcDiscount();

    /**
     * Gets the value of price
     *
     * @return the value of price
     */
    public double getPrice() {
        return price;
    }

    /**
     * Gets the value of copies
     *
     * @return the value of copies
     */
    public int getCopies() {
        return copies;
    }
}

具體策略角色:

package com.charon.strategy;

/**
 * @className: NoDiscountStrategy
 * @description:
 * @author: charon
 * @create: 2022-04-10 11:48
 */
public class NoDiscountStrategy extends DiscountStrategy{
    @Override
    double calcDiscount() {
        return 0;
    }
}

package com.charon.strategy;

/**
 * @className: FlatRateStrategy
 * @description:
 * @author: charon
 * @create: 2022-04-10 11:48
 */
public class FlatRateStrategy extends DiscountStrategy{

    /**
     * 折扣金額
     */
    private int discountPrice;

    public FlatRateStrategy(double price, int copies) {
        super(price,copies);
    }

    /**
     * Sets the discountPrice
     *
     * @param discountPrice discountPrice
     */
    public void setDiscountPrice(int discountPrice) {
        this.discountPrice = discountPrice;
    }

    @Override
    double calcDiscount() {
        return discountPrice * getCopies();
    }
}

package com.charon.strategy;

/**
 * @className: PercentageStrategy
 * @description:
 * @author: charon
 * @create: 2022-04-10 11:49
 */
public class PercentageStrategy extends DiscountStrategy{

    /**
     * 折扣百分比
     */
    private double discountPercent;

    public PercentageStrategy(double price, int copies) {
        super(price, copies);
    }

    /**
     * Sets the discountPercent
     *
     * @param discountPercent discountPercent
     */
    public void setDiscountPercent(double discountPercent) {
        this.discountPercent = discountPercent;
    }

    @Override
    double calcDiscount() {
        return getCopies() * getPrice() * discountPercent;
    }
}

環境角色類:

package com.charon.strategy;

/**
 * @className: Book
 * @description:
 * @author: charon
 * @create: 2022-04-10 12:15
 */
public class Book {

    /**
     * 圖書名稱
     */
    private String name;

    /**
     * 折扣型別
     */
    private DiscountStrategy strategy;

    public Book(String name, DiscountStrategy strategy) {
        this.name = name;
        this.strategy = strategy;
    }

    /**
     * Sets the strategy
     *
     * @param strategy strategy
     */
    public void setStrategy(DiscountStrategy strategy) {
        this.strategy = strategy;
    }

    public void getDiscount(){
        System.out.println("圖書名稱:《" + name + "》,折扣演算法為:"+ strategy.getClass() +",折扣價格為:" + strategy.calcDiscount());
    }
}

客戶端角色:

package com.charon.strategy;

/**
 * @className: Client
 * @description:
 * @author: charon
 * @create: 2022-04-10 12:14
 */
public class Client {

    public static void main(String[] args) {
        Book book1 = new Book("java設計模式", new NoDiscountStrategy());
        book1.getDiscount();

        FlatRateStrategy rateStrategy = new FlatRateStrategy(23.0, 5);
        rateStrategy.setDiscountPrice(1);
        Book book2 = new Book("java與模式",rateStrategy);
        book2.getDiscount();

        System.out.println("修改《java與模式》的折扣演算法:");
        PercentageStrategy percentageStrategy = new PercentageStrategy(23, 5);
        percentageStrategy.setDiscountPercent(0.07);
        book2.setStrategy(percentageStrategy);
        book2.getDiscount();
    }
}

列印:
    圖書名稱:《java設計模式》,折扣演算法為:class com.charon.strategy.NoDiscountStrategy,折扣價格為:0.0
    圖書名稱:《java與模式》,折扣演算法為:class com.charon.strategy.FlatRateStrategy,折扣價格為:5.0
    修改《java與模式》的折扣演算法:
    圖書名稱:《java與模式》,折扣演算法為:class com.charon.strategy.PercentageStrategy,折扣價格為:8.05

策略模式的主要優點如下:

  1. 多重條件語句不易維護,而使用策略模式可以避免使用多重條件語句,如 if...else 語句、switch...case 語句。
  2. 策略模式提供了一系列的可供重用的演算法族,恰當使用繼承可以把演算法族的公共程式碼轉移到父類裡面,從而避免重複的程式碼。
  3. 策略模式可以提供相同行為的不同實現,客戶可以根據不同時間或空間要求選擇不同的。
  4. 策略模式提供了對開閉原則的完美支援,可以在不修改原始碼的情況下,靈活增加新演算法。
  5. 策略模式把演算法的使用放到環境類中,而演算法的實現移到具體策略類中,實現了二者的分離。

策略模式的主要缺點如下:

  1. 客戶端必須理解所有策略演算法的區別,以便適時選擇恰當的演算法類。
  2. 策略模式造成很多的策略類,增加維護難度。

策略模式的應用場景

策略模式在很多地方用到,如 Java SE 中的容器佈局管理就是一個典型的例項,Java SE 中的每個容器都存在多種佈局供使用者選擇。在程式設計中,通常在以下幾種情況中使用策略模式較多。

  1. 一個系統需要動態地在幾種演算法中選擇一種時,可將每個演算法封裝到策略類中。
  2. 一個類定義了多種行為,並且這些行為在這個類的操作中以多個條件語句的形式出現,可將每個條件分支移入它們各自的策略類中以代替這些條件語句。
  3. 系統中各演算法彼此完全獨立,且要求對客戶隱藏具體演算法的實現細節時。
  4. 系統要求使用演算法的客戶不應該知道其操作的資料時,可使用策略模式來隱藏與演算法相關的資料結構。
  5. 多個類只區別在表現行為不同,可以使用策略模式,在執行時動態選擇具體要執行的行為。