1. 程式人生 > >初探Java設計模式3:行為型模式(策略,觀察者等)

初探Java設計模式3:行為型模式(策略,觀察者等)

轉自https://javadoop.com/post/design-pattern行為型模式行為型模式

行為型模式關注的是各個類之間的相互作用,將職責劃分清楚,使得我們的程式碼更加地清晰。

策略模式

策略模式太常用了,所以把它放到最前面進行介紹。它比較簡單,我就不廢話,直接用程式碼說事吧。

下面設計的場景是,我們需要畫一個圖形,可選的策略就是用紅色筆來畫,還是綠色筆來畫,或者藍色筆來畫。

首先,先定義一個策略介面:

public interfaceStrategy{
   publicvoiddraw(int radius, int x, int y);
}

然後我們定義具體的幾個策略:

public classRedPenimplementsStrategy{
   @Override
publicvoiddraw(int radius, int x, int y){ System.out.println("用紅色筆畫圖,radius:" + radius + ", x:" + x + ", y:" + y); } } public classGreenPenimplementsStrategy{ @Override publicvoiddraw(int radius, int x, int y){ System.out.println("用綠色筆畫圖,radius:" + radius + ", x:" + x + ", y:"
+ y); } } public classBluePenimplementsStrategy{ @Override publicvoiddraw(int radius, int x, int y){ System.out.println("用藍色筆畫圖,radius:" + radius + ", x:" + x + ", y:" + y); } }

使用策略的類:

public classContext{
   private Strategy strategy;

   publicContext(Strategy strategy){
      this
.strategy = strategy; } publicintexecuteDraw(int radius, int x, int y){ return strategy.draw(radius, x, y); } }

客戶端演示:

publicstaticvoidmain(String[] args){
    Context context = new Context(new BluePen()); // 使用綠色筆來畫
      context.executeDraw(10, 0, 0);
}

放到一張圖上,讓大家看得清晰些:

strategy-1

這個時候,大家有沒有聯想到結構型模式中的橋樑模式,它們其實非常相似,我把橋樑模式的圖拿過來大家對比下:

bridge-1

要我說的話,它們非常相似,橋樑模式在左側加了一層抽象而已。橋樑模式的耦合更低,結構更復雜一些。

觀察者模式

觀察者模式對於我們來說,真是再簡單不過了。無外乎兩個操作,觀察者訂閱自己關心的主題和主題有資料變化後通知觀察者們。

首先,需要定義主題,每個主題需要持有觀察者列表的引用,用於在資料變更的時候通知各個觀察者:

public classSubject{

   private List<Observer> observers = new ArrayList<Observer>();
   private int state;

   publicintgetState(){
      return state;
   }

   publicvoidsetState(int state){
      this.state = state;
      // 資料已變更,通知觀察者們
      notifyAllObservers();
   }

   publicvoidattach(Observer observer){
      observers.add(observer);        
   }

   // 通知觀察者們
   publicvoidnotifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   }     
}

定義觀察者介面:

public abstract classObserver{
   protected Subject subject;
   publicabstractvoidupdate();
}

其實如果只有一個觀察者類的話,介面都不用定義了,不過,通常場景下,既然用到了觀察者模式,我們就是希望一個事件出來了,會有多個不同的類需要處理相應的資訊。比如,訂單修改成功事件,我們希望發簡訊的類得到通知、發郵件的類得到通知、處理物流資訊的類得到通知等。

我們來定義具體的幾個觀察者類:

public classBinaryObserverextendsObserver{

      // 在構造方法中進行訂閱主題
    publicBinaryObserver(Subject subject){
        this.subject = subject;
        // 通常在構造方法中將 this 釋出出去的操作一定要小心
        this.subject.attach(this);
    }

      // 該方法由主題類在資料變更的時候進行呼叫
    @Override
    publicvoidupdate(){
        String result = Integer.toBinaryString(subject.getState());
        System.out.println("訂閱的資料發生變化,新的資料處理為二進位制值為:" + result);
    }
}

public classHexaObserverextendsObserver{

    publicHexaObserver(Subject subject){
        this.subject = subject;
        this.subject.attach(this);
    }

    @Override
    publicvoidupdate(){
          String result = Integer.toHexString(subject.getState()).toUpperCase();
        System.out.println("訂閱的資料發生變化,新的資料處理為十六進位制值為:" + result);
    }
}

客戶端使用也非常簡單:

publicstaticvoidmain(String[] args){
    // 先定義一個主題
      Subject subject1 = new Subject();
      // 定義觀察者
      new BinaryObserver(subject1);
      new HexaObserver(subject1);

      // 模擬資料變更,這個時候,觀察者們的 update 方法將會被呼叫
      subject.setState(11);
}

output:

訂閱的資料發生變化,新的資料處理為二進位制值為:1011
訂閱的資料發生變化,新的資料處理為十六進位制值為:B

當然,jdk 也提供了相似的支援,具體的大家可以參考 java.util.Observable 和 java.util.Observer 這兩個類。

實際生產過程中,觀察者模式往往用訊息中介軟體來實現,如果要實現單機觀察者模式,筆者建議讀者使用 Guava 中的 EventBus,它有同步實現也有非同步實現,本文主要介紹設計模式,就不展開說了。

責任鏈模式

責任鏈通常需要先建立一個單向連結串列,然後呼叫方只需要呼叫頭部節點就可以了,後面會自動流轉下去。比如流程審批就是一個很好的例子,只要終端使用者提交申請,根據申請的內容資訊,自動建立一條責任鏈,然後就可以開始流轉了。

有這麼一個場景,使用者參加一個活動可以領取獎品,但是活動需要進行很多的規則校驗然後才能放行,比如首先需要校驗使用者是否是新使用者、今日參與人數是否有限額、全場參與人數是否有限額等等。設定的規則都通過後,才能讓使用者領走獎品。

如果產品給你這個需求的話,我想大部分人一開始肯定想的就是,用一個 List 來存放所有的規則,然後 foreach 執行一下每個規則就好了。不過,讀者也先別急,看看責任鏈模式和我們說的這個有什麼不一樣?

首先,我們要定義流程上節點的基類:

public abstract classRuleHandler{

      // 後繼節點
    protected RuleHandler successor;

    publicabstractvoidapply(Context context);

    publicvoidsetSuccessor(RuleHandler successor){
        this.successor = successor;
    }
    public RuleHandler getSuccessor(){
        return successor;
    }
}

接下來,我們需要定義具體的每個節點了。

校驗使用者是否是新使用者:

public classNewUserRuleHandlerextendsRuleHandler{

    publicvoidapply(Context context){
        if (context.isNewUser()) {
              // 如果有後繼節點的話,傳遞下去
            if (this.getSuccessor() != null) {
                this.getSuccessor().apply(context);
            }
        } else {
            throw new RuntimeException("該活動僅限新使用者參與");
        }
    }

}

校驗使用者所在地區是否可以參與:

public classLocationRuleHandlerextendsRuleHandler{
    publicvoidapply(Context context){
        boolean allowed = activityService.isSupportedLocation(context.getLocation);
          if (allowed) {
            if (this.getSuccessor() != null) {
                this.getSuccessor().apply(context);
            }
        } else  {
            throw new RuntimeException("非常抱歉,您所在的地區無法參與本次活動");
        }
    }
}

校驗獎品是否已領完:

public classLimitRuleHandlerextendsRuleHandler{
    publicvoidapply(Context context){
          int remainedTimes = activityService.queryRemainedTimes(context); // 查詢剩餘獎品
        if (remainedTimes > 0) {
            if (this.getSuccessor() != null) {
                this.getSuccessor().apply(userInfo);
            }
        } else {
            throw new RuntimeException("您來得太晚了,獎品被領完了");
        }
    }
}

客戶端:

publicstaticvoidmain(String[] args){
    RuleHandler newUserHandler = new NewUserRuleHandler();
      RuleHandler locationHandler = new LocationRuleHandler();
      RuleHandler limitHandler = new LimitRuleHandler();

      // 假設本次活動僅校驗地區和獎品數量,不校驗新老使用者
      locationHandler.setSuccessor(limitHandler);
      locationHandler.apply(context);
}

程式碼其實很簡單,就是先定義好一個連結串列,然後在通過任意一節點後,如果此節點有後繼節點,那麼傳遞下去。

至於它和我們前面說的用一個 List 存放需要執行的規則的做法有什麼異同,留給讀者自己琢磨吧。

模板方法模式

在含有繼承結構的程式碼中,模板方法模式是非常常用的,這也是在開原始碼中大量被使用的。

通常會有一個抽象類:

public abstract classAbstractTemplate{
    // 這就是模板方法
      publicvoidtemplateMethod(){
        init();
        apply(); // 這個是重點
        end(); // 可以作為鉤子方法
    }
    protectedvoidinit(){
        System.out.println("init 抽象層已經實現,子類也可以選擇覆寫");
    }
      // 留給子類實現
    protectedabstractvoidapply();
    protectedvoidend(){
    }
}

模板方法中呼叫了 3 個方法,其中 apply() 是抽象方法,子類必須實現它,其實模板方法中有幾個抽象方法完全是自由的,我們也可以將三個方法都設定為抽象方法,讓子類來實現。也就是說,模板方法只負責定義第一步應該要做什麼,第二步應該做什麼,第三步應該做什麼,至於怎麼做,由子類來實現。

我們寫一個實現類:

public classConcreteTemplateextendsAbstractTemplate{
    publicvoidapply(){
        System.out.println("子類實現抽象方法 apply");
    }
      publicvoidend(){
        System.out.println("我們可以把 method3 當做鉤子方法來使用,需要的時候覆寫就可以了");
    }
}

客戶端呼叫演示:

publicstaticvoidmain(String[] args){
    AbstractTemplate t = new ConcreteTemplate();
      // 呼叫模板方法
      t.templateMethod();
}

程式碼其實很簡單,基本上看到就懂了,關鍵是要學會用到自己的程式碼中。

狀態模式

update: 2017-10-19

廢話我就不說了,我們說一個簡單的例子。商品庫存中心有個最基本的需求是減庫存和補庫存,我們看看怎麼用狀態模式來寫。

核心在於,我們的關注點不再是 Context 是該進行哪種操作,而是關注在這個 Context 會有哪些操作。

定義狀態介面:

            
           

相關推薦

初探Java設計模式3行為模式策略觀察

轉自https://javadoop.com/post/design-pattern行為型模式行為型模式行為型模式關注的是各個類之間的相互作用,將職責劃分清楚,使得我們的程式碼更加地清晰。策略模式策略模式太常用了,所以把它放到最前面進行介紹。它比較簡單,我就不廢話,直接用程式

Java設計模式簡介行為模式

其實每個設計模式都是很重要的一種思想,看上去很熟,其實是因為我們在學到的東西中都有涉及,儘管有時我們並不知道,其實在Java本身的設計之中處處都有體現,像AWT、JDBC、集合類、IO管道或者是Web框架,裡面設計模式無處不在。因為我們篇幅有限,很難講每一個設計模式都講的很詳細。 本章講講

Java設計模式簡介行為模式

本章講到第三種設計模式——行為型模式,共11種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、直譯器模式。 先來張圖,看看這11中模式的關係: 第一類:通過父類與子類的關係進行實現。第二類:兩個類之間。第三類:類的狀態。第

人人都能讀懂的設計模式3行為模式

用最簡單的語言,解釋設計模式。 雖然示例程式碼是用 PHP7 實現的,但因為概念是一樣的,所以語言並不會阻礙大家理解設計模式。 概述 行為型設計模式關心物件之間的責任分配。與結構型設計模式不同的是,行為型設計模式不僅僅指定結構,而且還概述了它們之間的訊息傳遞/通訊

Java設計模式建立模式—建造模式

1.建造者模式定義 建造者模式,顧名思義的就是類似建房子,有一個固定的流程。在大話設計模式中,作者舉了一個例子大概意思是同一道菜在中國的每一個地方都有不同的味道(LZ印象最深的是酸菜魚,來杭州之後印象最深刻的是清蒸鱸魚。),而肯德基的雞腿、漢堡在每一個城市都是一樣的味道。建造者模式實

Java設計模式建立模式—原型模式

1.原型模式(Prototype Pattern) 定義: 原型(Prototype)模式是一種物件建立型模式,他採取複製原型物件的方法來建立物件的例項。使用原型模式建立的例項,具有與原型一樣的資料。 原型模式的特點: 由原型物件自身建立目標物件。也就是說,物件建立這

js的36個設計模式行為模式

1.模板方法模式 不太好描述,就類似封裝一個外掛,傳入不同引數和函式,實現新增dom的樣式和方法。 2.觀察者模式 跳過 3.狀態模式 把多個判斷封裝到函式內。 var a = "run"; function action(a){

Php設計模式行為模式

可以線上執行檢視效果哦!     <接上一篇> 4、觀察者模式(Observer):          又叫釋出訂閱模式,當一個主體物件發生改變時,依賴它的多個觀察者物件都得到通知並自動更新響應。就像報社一樣,今天釋出的訊息只要是看這份報紙的人看到的都是同樣的內容。如果釋出另一份報紙,也是一

設計模式的藝術 行為模式之訪問者模式

前言 在公司上班,一般會有兼職或全職的員工,他們都發工資,上同樣的班,但是工資待遇是有區別的,財務部和人事部過來調查處理的手法也不是一樣的,雖然都是一樣的計算工資待遇,在軟體開發中存在著這樣的一種情況,我們需要處理著像員工一樣的集合,集合中的具體物件是不一樣的,去訪問時處理的手段也不一樣,軟體設

設計模式的藝術 行為模式之模板方法模式

前言 生活中有許多事情可以理解成分步驟執行的東西,比如請客吃飯,無論吃什麼,一般都包含著點單,吃東西,買單幾個步驟,不論吃麵還是吃大餐,其他步驟不會變,最多變變點單,在軟體開發中,也會有類似的情況出現,我們可以有個點單的基類,然後子類裡面具體實現是吃麵還是點大餐,這就是模板方法模式,這種模式利用

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

前言 條條大路通羅馬,很多時候為了達到目標可供選擇的路徑不止一條,在軟體開發中也會存在這樣的情況,為了某一個功能有多條途徑,每一條途徑對應一種演算法,為了靈活的選擇解決途徑,策略模式就應運而生了 什麼是策略模式 Strategy Pattern 定義一系列演算法類,將每一個演算法封裝起來

設計模式的藝術 行為模式觀察模式

前言 紅燈停,綠燈行,在日常的交通中,每每遇到紅燈,司機總是要在路口進行等待,等到綠燈才能通過,這個時候司機就扮演了一個觀察者的角色,隨著燈的顏色的變化,司機的行為也跟著變化,在軟體系統中,有些物件之間也存在類似交通訊號燈和汽車之間的關係,一個物件的的行為狀態改變導致了其他物件的狀態或行為也發生

設計模式的藝術 行為模式之備忘錄模式

前言 每個人都會有後悔的時候,可是人生沒有後悔藥,做過的事情無法再去後悔,軟體設計中卻是有這麼一種後悔機制,叫做備忘錄模式,它就是軟體中的"後悔藥" 什麼是備忘錄模式  Memento Pattern 在不破壞封裝的前提下,捕獲一個物件的內部狀態,並在物件之外儲存這個狀態,這樣

設計模式的藝術 行為模式之中介模式

前言 微信有一個群聊功能,群聊中的資訊你不需要加別人為好友就可以接收的到資訊,因為群的機制,極大的減少了使用者之間的兩兩通訊,在軟體設計中,也有類似於微信使用者之間的關係,我們也可以借鑑微信群的模式,來降低物件與物件之間的互動,使得系統的耦合度大大降低 什麼是中介者模式 Mediator P

設計模式的藝術 行為模式之迭代器模式

前言 現在的電視機都配置了一個遙控器,使用者可以通過遙控器去選擇上一個或者下一個臺,我們只需要知道如何使用這個遙控器,而無須關注電視是怎麼把電視訊道放入其中的,在軟體實際的開發中,也有這麼一種類,它儲存著多個成員物件,這些類通常稱為聚合類,對應的物件稱為聚合物件。為了方便操作這些聚合物件,同時可

設計模式的藝術 行為模式之直譯器模式

前言 目前計算器程式語言有好幾百種,但是有時候人們還是希望能用一些簡單的語言表達實現一些特定的操作,比如輸入一個檔案,它就可以按照預定的格式進行解釋,從而實現相應的功能。 在現實的開發中,這些簡單的自定義語言可以通過現有的程式語言來設計,如果所基於的程式語言是面嚮物件語言,此時可以使用直譯器

設計模式的藝術 行為模式之命令模式

前言 裝修新房子的最後幾道工序之一是安裝插座和開關,通過開關可以控制一些電器的開啟和關閉,例如電燈或者排氣扇,但是購買開關時其實並不知道它能夠控制什麼具體的電器。在軟體設計中,也存在著這樣的關係,請求傳送者和接收者物件,為了降低之間的耦合度,我們會將兩者進行解耦,會在兩者之間引入了新的命令物件,

設計模式的藝術 行為模式之職責鏈模式

前言 幾乎所有的國人都打過鬥地主,規則很簡單,上家出牌,下家接牌,要不起則轉給下一家,一個迴圈下來,如果要不起則最初的出牌者繼續出牌,在這個過程中,牌作為一個請求沿著一條鏈在傳遞,每一個紙牌的玩家都可以處理該請求,在設計模式中,也有一種專門用於處理這種請求鏈方式的傳遞模式,我們把它稱之為職責鏈模

設計模式分類之行為模式

行為型模式包括觀察者模式、模板方法模式、命令模式、狀態模式、職責鏈模式、直譯器模式、中介者模式、訪問者模式、策略模式、備忘錄模式、迭代器模式。 觀察者模式 定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴它的物件都得到通知並被自動更新。 模板方法模式

Java Challengers#3性和繼承

    根據Venkat Subramaniam的傳說,多型性是面向物件程式設計中最重要的概念。多型性 -或者物件基於其型別執行專門操作的能力 - 是使Java程式碼具有靈活性的原因。命令,觀察者,裝飾者,策略等設計模式以及Gang Of Four建立的許多其他模式都使用