1. 程式人生 > >設計模式的藝術 行為型模式之策略模式

設計模式的藝術 行為型模式之策略模式

前言

條條大路通羅馬,很多時候為了達到目標可供選擇的路徑不止一條,在軟體開發中也會存在這樣的情況,為了某一個功能有多條途徑,每一條途徑對應一種演算法,為了靈活的選擇解決途徑,策略模式就應運而生了

什麼是策略模式 Strategy Pattern

定義一系列演算法類,將每一個演算法封裝起來,並讓它們可以相互替換,策略模式讓演算法獨立於使用它的客戶而變化,策略模式也被稱之為政策模式,策略模式是一種物件行為型模式

策略模式的優點

(1)、策略模式提供了對開閉原則的完美支援,使用者可以在不修改原有系統的基礎上選擇演算法或者行為,也可以靈活的增加新的演算法或行為

(2)、策略模式提供了管理相關的演算法族的辦法,策略類的等級結構定義了一個演算法或者行為族,恰當使用繼承可以把公共的程式碼移到抽象策略類中,從而避免重複的程式碼

(3)、策略模式提供了一種可以替換繼承關係的方法,如果不使用策略模式,那麼使用演算法的環境類就可能會有一些子類,每一個子類提供一種不同的演算法。但是,這樣一來演算法的使用和演算法本身就混合在了一起,不符合單一職責原則,決定使用哪一種演算法的邏輯和該演算法本身混合在一起,從而不可能獨立演化;而且使用繼承無法實現演算法或行為在程式執行時的動態切換

(4)、使用策略模式可以避免多重條件選擇語句。多重條件選擇語句不易維護,它把採取哪一種演算法或者行為的邏輯或行為本身的實現邏輯混合在了一起,將它們都以硬編碼的方式放入同一個龐大的多重條件選擇中,比直接繼承環境類的辦法還要原始和落後

(5)、策略模式提供了一種演算法的複用機制,由於將演算法單獨提取出來封裝在策略類中,因此不同的環境類可以方便的複用這些策略類

策略模式的缺點

(1)、客戶端必須知道所有的策略類,並自行決定使用哪一個策略類,這就意味著客戶端必須理解這些演算法的區別,以便適時的選擇恰當的演算法。換言之,策略模式只適用於客戶端知道所有的演算法或行為的情況

(2)、策略模式將造成系統產生很多的具體策略類,任何細小的變化都將會導致系統要增加一個新的具體策略類

(3)、無法同時在客戶端使用多個策略類。也就是說,在使用策略模式時,客戶端每次只能使用一個策略類,不支援使用一個策略類完成部分功能然後再使用另一個策略類完成剩下的功能的情況

策略模式的使用場景

(1)、系統需要動態的在幾種演算法中選擇一種,那麼可以將這些演算法封裝到一個個的具體演算法中,而這些具體演算法類都是一個抽象演算法類的子類,換言之,這些具體演算法類均有統一的介面,根據里氏替換原則和麵向物件的多型性,客戶端可以使用任何一個具體演算法類,並只需要維持一個數據型別是抽象演算法類的物件

(2)、一個物件有很多的行為,如果不用恰當的模式,這些行為就只好使用多重條件選擇語句來實現,此時,使用策略模式,把這些行為轉移到相應的具體策略類裡面,就可以避免使用難以維護的多重條件選擇語句。

(3)、不希望客戶端知道複雜,與演算法相關的資料結構,在具體策略類中封裝演算法與相關的資料結構,可以提高演算法的保密性和安全性

策略模式的具體實現

目錄結構

抽象狀態類

package com.company;

//抽象狀態類
public abstract class AccountState {
    protected Account acc;

    public abstract void deposit(double amount);

    public abstract void withdraw(double amount);

    public abstract void computerInterest();

    public abstract void stateCheck();
}

具體狀態類

package com.company;

//正常狀態:具體狀態類
public class NormalState extends AccountState {

    public NormalState(Account account) {
        this.acc=account;
    }

    public NormalState(AccountState overDraftState) {
        this.acc=overDraftState.acc;
    }

    @Override
    public void deposit(double amount) {
            acc.setBalance(acc.getBalance()+amount);
            stateCheck();
    }

    @Override
    public void withdraw(double amount) {
            acc.setBalance(acc.getBalance()-amount);
            stateCheck();
    }

    @Override
    public void computerInterest() {
        System.out.println("正常裝填,無須支付利息");
    }

    @Override
    public void stateCheck() {
        if(acc.getBalance()>-2000 && acc.getBalance()<=0){
            acc.setState(new OverDraftState(this));
        }else if(acc.getBalance()==-2000){
            acc.setState(new RestrictedState(this));
        }else if(acc.getBalance()<-2000) {
            System.out.println("操作受限!");
        }
    }
}
package com.company;

//透支狀態:具體狀態類
public class OverDraftState extends  AccountState {
    public OverDraftState(AccountState state) {
        this.acc=state.acc;
    }

    @Override
    public void deposit(double amount) {
        acc.setBalance(acc.getBalance()+amount);
        stateCheck();
    }

    @Override
    public void withdraw(double amount) {
        acc.setBalance(acc.getBalance()-amount);
        stateCheck();
    }

    @Override
    public void computerInterest() {
        System.out.println("計算利息!");
    }

    @Override
    public void stateCheck() {
        if(acc.getBalance()>0){
            acc.setState(new NormalState(this));
            acc.setState(new RestrictedState(this));
        }else if(acc.getBalance()==-2000){
        }else if(acc.getBalance()<-2000){
            System.out.println("操作受限!");
        }
    }
}
package com.company;

//受限狀態:具體狀態類
public class RestrictedState extends AccountState {

    public RestrictedState(AccountState state) {
        this.acc=state.acc;
    }


    @Override
    public void deposit(double amount) {
        acc.setBalance(acc.getBalance()+amount);
        stateCheck();
    }

    @Override
    public void withdraw(double amount) {
            System.out.println("賬號受限,取款失敗");
    }

    @Override
    public void computerInterest() {
        System.out.println("計算利息!");
    }

    @Override
    public void stateCheck() {
        if(acc.getBalance()>0){
            acc.setState(new NormalState(this));
        }else if(acc.getBalance()>-2000){
            acc.setState(new OverDraftState(this));
        }
    }
}

環境類

package com.company;

import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;

//銀行賬戶:環境類
public class Account {
    private AccountState state;  //維持一個對抽象狀態物件的引用
    private String owner;   //開戶名
    private double balance;  //賬戶餘額s

    public Account(String owner, double balance) {
        this.owner = owner;
        this.balance = balance;
        this.state = new NormalState(this);  //設定初始狀態
        System.out.println(this.owner + "開戶,初始金額為" + balance);
        System.out.println("------------------------");

    }

    public void deposit(double amount) {
        System.out.println(this.owner + "存款" + amount);
        state.deposit(amount);  //呼叫狀態物件的deposit()方法
        System.out.println("現在餘額為" + this.balance);
        System.out.println("現在賬戶狀態為" + this.state.getClass().getName());
        System.out.println("---------------------------");
    }

    public void withdraw(double amount) {
        System.out.println(this.owner + "取款" + amount);
        state.withdraw(amount);  //呼叫狀態物件的withdraw()方法
        System.out.println("現在餘額為" + this.balance);
        System.out.println("現在賬戶狀態為" + this.state.getClass().getName());
        System.out.println("-------------------------------");
    }

    public void computeInterest() {
        state.computerInterest();  //呼叫狀態物件的computeInterest()方法
    }

    public AccountState getState() {
        return state;
    }

    public void setState(AccountState state) {
        this.state = state;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}

測試端

package com.company;

public class Main {

    public static void main(String[] args) {
    Account acc=new Account("段譽",0.0);
    acc.deposit(1000);
    acc.withdraw(200);
    acc.deposit(1000);
    acc.withdraw(4000);
    acc.withdraw(1000);
    acc.computeInterest();
    }
}

轉載請註明出處,掌聲送給社會人