設計模式之行爲型

前言

最近加班是真的不少,沒法騰出大塊時間來學習。設計模式又不想只更到一半半途而廢,想了又想,決定精簡,保證你們一看就懂(看完就忘...)。設計模式分建立型模式,結構型模式和行爲型模式。到目前爲止,建立型模式已經講完,對於剩下的模式,會分紅這兩大塊統一講解。算法

行爲型模式

行爲型模式主要關注的點事類的動做,各個類之間相互的做用,將職責劃分清楚,使咱們的代碼更加的清晰。數據庫


策略模式

定義一組算法,將每一個算法都封裝起來,而且使他們之間能夠互換。
策略模式是一個出現頻率很高,但又很簡單的模式。下面的場景是咱們要出去旅遊,可是能夠選擇出去旅遊的交通方式,好比坐飛機,坐火車或者步行。廢話再也不多說,直接上碼:
public interface Strategy {
    void travel();
}
public class Walk implements Strategy {
    @Override
    public void travel() {
        System.out.println("步行去旅行");
    }
}
public class Train implements Strategy {
    @Override
    public void travel() {
        System.out.println("坐火車去旅行");
    }
}
public class Airplane implements Strategy {
    @Override
    public void travel() {
        System.out.println("坐着飛機去旅行");
    }
}
public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    public void execute(){
        strategy.travel();
    }
}
public class Client {
    public static void main(String[] args) {
        Context context = new Context(new Airplane());
        context.execute();
    }
}
console:
坐着飛機去旅行

策略模式的優勢很是明顯,在現有的系統中增長一個策略太容易,只要實現接口就能夠了,其餘都不用修改,相似於一個能夠反覆拆卸的插件,符合ocp原則。其缺點就是每一個策略都是一個類,複用性很小,複雜的業務場景容易發生類數量爆炸,而且策略模式和迪米特法則是違背的,咱們看下上面的clent場景類(至關於項目中的高層調用模塊),我只是想使用一個策略,憑什麼就要了解這個策略呢?那要封裝類就沒有意義了,這是策略模式的一個大缺點,因此策略模式不多單獨出現,大多結合其餘模式來彌補這個缺陷,如工廠方法或者代理模式。segmentfault


觀察者模式

定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則全部依賴於它的對象都會獲得通知並被自動更新

策略模式也叫發佈訂閱模式,其中最主要額角色名稱就是subject被觀察者和observer觀察者。接下來會模擬一個場景,學校做爲被觀察者發佈放假的消息,家長和學生做爲觀察者實現本身的邏輯:設計模式

/**
 * 主題(被觀察者)須要實現的職責,就是能夠動態的增長刪除觀察者。
 * 根據主題的狀態通知全部的觀察者Observer
 */
public abstract class Subject {
    private Vector<Observer> observerList = new Vector<>();

    private int status;

    public void addObserver(Observer observer){
        observerList.add(observer);
    }

    public void delObserver(Observer observer){
        observerList.remove(observer);
    }
    public void notifyAllObserver(){
        observerList.forEach(observer -> {
            observer.update();
        });
    }

}
public class SchoolSubject extends Subject {
    //學校宣佈放七天假期
    public void haveSevenDaysHoliday(){
        System.out.println("學校:從今天開始放假,全部學生七天後返校");
        super.notifyAllObserver();
    }
}
public interface Observer {
    //觀察者,被通知了實現其本身的邏輯
     void update() ;
}
public class Parent implements Observer {
    @Override
    public void update() {
        System.out.println("家長:這倒黴孩子怎麼又放假了,堅定不能讓他玩王者榮耀....");
    }
}
public class Student implements Observer {
    @Override
    public void update() {
        System.out.println("學生:哇哈哈,終於有時間打王者榮耀嘍");
    }
}
public class Client {

    public static void main(String[] args) {
        SchoolSubject subject = new SchoolSubject();
        Student student = new Student();
        Parent parent = new Parent();
        subject.addObserver(student);
        subject.addObserver(parent);
        subject.haveSevenDaysHoliday();
    }

}
console:
學校:從今天開始放假,全部學生七天後返校
學生:哇哈哈,終於有時間打王者榮耀嘍
家長:這倒黴孩子怎麼又放假了,堅定不能讓他玩王者榮耀....

雖然說觀察者和被觀察者是耦合在一塊兒的,可是不論是擴展增長觀察者仍是被觀察者都很是容易。而且根據單一職責原則,每一個類的職責都是惟一,須要一套機制將類串聯起來造成一個真實的場景,就好比學校公佈放假,孩子想着玩遊戲,家長爲了孩子的成績禁止孩子玩遊戲,而後由於學校放假我就不玩了(小學生,你懂得),這樣就造成了一個觸發機制。其缺點就是執行效率低下,需異步執行。從原理上看,咱們經常使用的mq就是觀察者模式的升級版。異步

責任鏈模式

使多個對象都有機會處理請求,從而避免了請求的發送者和接受者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。

責任鏈咱們很容易想到鏈表結構,實際上責任鏈就是一種基於鏈表的處理方式。當一個請求過來,調用鏈表的頭結點,處理以後再日後流轉。
有這麼一個場景:路飛餓了打開美團準備訂外賣。選中吃的後,進行下單,首先校驗是否在營業時間內,而後校驗是否在配送範圍內,而後校驗是否有貨等等,設定責任鏈都經過後,路飛才能訂到飯。ide

//鏈表內結點的基類
public abstract class RuleHandler {

    protected RuleHandler successor;

    public abstract void echo(Context context);

    public void setSuccessor(RuleHandler successor) {
        this.successor = successor;
    }

    public RuleHandler getSuccessor() {
        return successor;
    }
}
//判斷營業時間
public class TimeHandler extends RuleHandler {
    @Override
    public void echo(Context context) {
        //營業時間判斷
        if (context.isTimeInRange()){
            if (this.getSuccessor()!=null){
                this.getSuccessor().echo(context);
            }
        }else {
            throw new RuntimeException("不在營業時間內");
        }
    }
}
//判斷是否在配送範圍內
public class AreaHanler extends RuleHandler {
    @Override
    public void echo(Context context) {
        if (context.isAreaInRange()){
            if (this.getSuccessor()!=null){
                this.getSuccessor().echo(context);
            }
        }else {
            throw  new RuntimeException("不在配送範圍內");
        }
    }
}
//判斷庫存
public class StockHandler extends RuleHandler {
    @Override
    public void echo(Context context) {
        if (context.hasStock()){
            if (this.getSuccessor()!=null){
                this.getSuccessor().echo(context);
            }
        }else {
            throw new RuntimeException("挑選的商品已經賣完");
        }
    }
}

客戶端調用邏輯:性能

public class Client {

    public static void main(String[] args) {
        RuleHandler timeHandler = new TimeHandler();
        RuleHandler areaHandler = new AreaHanler();
        RuleHandler stockHandler = new StockHandler();
        
        timeHandler.setSuccessor(areaHandler);
        areaHandler.setSuccessor(stockHandler);
        timeHandler.echo(new Context());
    }
    
}

代碼很是簡單,責任鏈模式的重點是在鏈上,由一條鏈去處理請求並返回相應的結果。它很是顯著的優勢就是將請求和處理分開,二者解耦,提升系統的靈活性。缺點就是鏈表遍歷必須從鏈頭到鏈尾,存在性能問題。採用了相似遞歸調用的方式,增大了讀懂邏輯的難度學習

模板方法模式

在以前的博客裏已經長篇大論過,故直接拿過來。
模板方法測試

狀態模式

當一個對象內在狀態改變時容許其改變行爲,這個對象看起來像改變了其類。

狀態模式的核心是封裝,經過狀態的變動引發行爲的變動。如今你們來思考一下,電腦有三種狀態,分別爲關機,已啓動。this

//表明環境,也就是狀態的主體
public class Context {

    //全部的電腦狀態
    public final static OpenState OPEN = new OpenState();
    public final static CloseState CLOSE = new CloseState();
    //電腦當前的狀態
    private ComputerState currentState;

    public ComputerState getCurrentState() {
        return currentState;
    }

    public void setCurrentState(ComputerState currentState) {
        this.currentState = currentState;
        this.currentState.setContext(this);
    }

    public void openMachine() {
        this.currentState.openMachine();
    }

    public void closeMachine() {
        this.currentState.closeMachine();
    }
}
//狀態基類,真實的電腦邏輯封裝在了狀態中
public abstract class ComputerState {

    protected Context context;

    public void setContext(Context context) {
        this.context = context;
    }
    public abstract void openMachine();

    public abstract void closeMachine();

}
public class OpenState extends ComputerState{
    @Override
    public void openMachine() {
        System.out.println("電腦開機...");
    }

    @Override
    public void closeMachine() {
        super.context.setCurrentState(Context.CLOSE);
        super.context.getCurrentState().closeMachine();
    }
}
public class CloseState extends ComputerState {
    @Override
    public void openMachine() {
        super.context.setCurrentState(Context.OPEN);
        super.context.getCurrentState().openMachine();
    }

    @Override
    public void closeMachine() {
        System.out.println("電腦關機...");
    }
}

客戶端測試類:

public class Client {
    public static void main(String[] args) {
        Context context = new Context();
        context.setCurrentState(Context.OPEN);
        context.openMachine();
        context.closeMachine();
    }
}

狀態模式的優勢有結構清晰,避免了各類條件的判斷,省掉了swtich...case,if...else語句的使用,提高了代碼的可讀性。遵循了單一職責原則和開閉原則,每一個狀態都是一個子類,增長狀態只需增長一個狀態子類,修改狀態,修改對應的子類就能夠了。封裝性很是好,客戶端不需知道內部狀態的轉換以及相應的邏輯.其缺點就是狀態子類會太多,而且咱們能夠將狀態存儲到數據庫中,而後根據狀態執行相應的操做,這也是一種不錯的實現方式,具體如何使用看你們我的喜愛了。

總結

本章的行爲型模式總結了策略模式、觀察者模式、責任鏈模式、模板方法模式和狀態模式,其實不只於此,還有備忘錄模式和命令模式等,但因其使用場景有限,就不作一一探討了,留給讀者本身學習~.~

相關文章
相關標籤/搜索