1. 程式人生 > >設計模式的藝術 行為型模式之觀察者模式

設計模式的藝術 行為型模式之觀察者模式

前言

紅燈停,綠燈行,在日常的交通中,每每遇到紅燈,司機總是要在路口進行等待,等到綠燈才能通過,這個時候司機就扮演了一個觀察者的角色,隨著燈的顏色的變化,司機的行為也跟著變化,在軟體系統中,有些物件之間也存在類似交通訊號燈和汽車之間的關係,一個物件的的行為狀態改變導致了其他物件的狀態或行為也發生變化,他們之間將進行聯動,正所謂"觸一而牽百發",為了更好的描述物件之間存在著這種一對多的聯動,觀察者模式就應運而生了。它定義了物件之間一種一對多的依賴關係,讓一個物件的改變能夠影響其他物件

什麼是觀察者模式呢? Observer Pattern

定義物件之間的一種一對多的依賴關係,使得每當一個物件狀態發生改變時,其相關的依賴物件皆得到通知並自動更新,觀察者模式的別名包括髮布-訂閱(Publish/Subscribe)模式,模型-檢視(Model/View)模式、源-監聽器(Source/Listener)模式或者從屬者(Dependents)模式。觀察者模式是一種物件行為型模式

觀察者模式的優點

(1)、觀察者模式可以實現表示層和資料邏輯層分離,定義了穩定的訊息更新傳遞機制,並抽象了更新介面,使得可以有各種各樣不同的表示層充當具體觀察者角色

(2)、觀察者模式在觀察目標和觀察者之間建立一個抽象的耦合。觀察目標只需要維持一個抽象觀察者的集合,無須瞭解其具體觀察者。由於觀察目標和觀察者沒有緊密的耦合在一起,因此他們可以屬於不同的抽象化層次

(3)、觀察者模式支援廣播通訊,觀察目標會向所有已註冊的觀察者物件傳送通知,簡化了一對多系統設計的難度

(4)、觀察者模式滿足開閉原則的要求,增加新的具體觀察者無須修改原有系統程式碼,在具體觀察者與觀察目標之間不存在關聯關係的情況下,增加新的觀察目標也很方便

觀察者模式的缺點

(1)、如果一個觀察物件目標有很多直接和簡介觀察者,將所有的觀察者都通知到會花費很多時間。

(2)、如果在觀察者和觀察目標之間存在迴圈依賴,觀察目標會觸發它們之間進行迴圈呼叫,可能導致系統崩潰

(3)、觀察者模式沒有相應的機制讓觀察者知道所觀察的目標是怎麼變化的,而僅僅只是知道觀察目標發生了變化

觀察者模式的適用場景

(1)、一個抽象模型有兩個方面,其中一個方面依賴於另一個方面,將這兩個方面封裝在獨立的物件中使它們可以各自獨立地改變和複用

(2)、一個物件的改變將導致一個或多個其他物件也發生改變,而並不知道具體有多少物件將發生改變,也不知道這些物件是誰

(3)、需要在系統中建立一個觸發鏈,A物件的行為將影響B物件,B物件的行為將影響C物件。。。。,可以使用觀察者模式建立一種鏈式觸發機制。

觀察者模式的具體實現

目錄結構

抽象觀察者

package com.company;

//抽象觀察者
public interface Observer {
    String getName();

    void setName(String name);

    void help();  //宣告支援盟友方法

    void beAttacked(AllyControllCenter aco);   //宣告遭受攻擊的方法
}

具體觀察者

package com.company;

//戰隊成員類:具體觀察者
public class Player implements Observer {
    private String name;

    public Player(String name) {
        this.name = name;
    }


    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name=name;
    }

    @Override
    //支援盟友的方法
    public void help() {
        System.out.println("堅持住,"+this.name+"來救你");
    }

    @Override
    //遭受攻擊方法的實現,當遭受攻擊時將呼叫戰隊控制中心類的通知方法notifyObserver()
    //通知盟友
    public void beAttacked(AllyControllCenter aco) {
            System.out.println(this.name+"被攻擊!");
            aco.notifyObserver(name);
    }
}

控制類

package com.company;

import java.util.ArrayList;

//戰隊控制中心類:目標類
public abstract class AllyControllCenter {
    protected String allyName;   //戰隊名稱
    protected ArrayList<Observer> players=new ArrayList<Observer>();  //定義一個集合儲存戰隊成員
    //註冊方法
    public void join(Observer abs){
        players.add(abs);
        System.out.println(abs.getName()+"加入"+this.allyName+"戰隊!");

    }
    //宣告抽象通知方法
    public abstract void notifyObserver(String name);

    //登出方法
    public void quit(Observer abs){
        System.out.println(abs.getName()+"退出"+this.allyName+"戰隊!");
        players.remove(abs);
    }



    public String getAllyName() {
        return allyName;
    }

    public void setAllyName(String allyName) {
        this.allyName = allyName;
    }

    public ArrayList<Observer> getPlayers() {
        return players;
    }

    public void setPlayers(ArrayList<Observer> players) {
        this.players = players;
    }
}

具體目標類

package com.company;

//具體戰隊控制中心類:具體目標類
public class ConcreteAllyControllCenter extends  AllyControllCenter{
    public ConcreteAllyControllCenter(String allyName){
        System.out.println(allyName+"戰隊組建成功!");
        System.out.println("--------------");
        this.allyName=allyName;
    }

    @Override
    //實現通知方法
    public void notifyObserver(String name) {
        System.out.println(this.allyName+"戰隊緊急通知,盟友"+name+"遭受敵人攻擊!");
        //遍歷觀察者集合,呼叫每一個盟友(自己除外)的支援方法
        for(Object abs:players){
            if(!((Observer)abs).getName().equalsIgnoreCase(name)){
                ((Observer) abs).help();
            }
        }
    }
}

測試類

package com.company;

public class Main {

    public static void main(String[] args) {
           //定義觀察目標物件
        AllyControllCenter acc;
        acc=new ConcreteAllyControllCenter("金庸群俠");
        //定義四個觀察者物件
        Observer player1,player2,player3,player4;
        player1=new Player("楊過");
        acc.join(player1);
        player2=new Player("令狐沖");
        acc.join(player2);
        player3=new Player("張無忌");
        acc.join(player3);
        player4=new Player("段譽");
        acc.join(player4);
        //某成員遭受攻擊
        player1.beAttacked(acc);
    }
}

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