1. 程式人生 > >設計模式的征途—15.觀察者(Observer)模式

設計模式的征途—15.觀察者(Observer)模式

在日常生活中,交通訊號燈指揮者日益擁擠的城市交通。紅燈亮,汽車停止;綠燈亮,汽車繼續前行;在這個過程中,交通訊號燈是汽車的觀察目標,而汽車則是觀察者。隨著交通訊號燈的變化,汽車的行為也會隨之變化,一盞交通訊號燈可以指揮多輛汽車。在軟體系統中,有些物件之間也存在類似交通訊號燈和汽車之間的關係,一個物件的狀態或行為的變化將會導致其他物件的狀態或者行為也發生改變,它們之間將產生聯動,正所謂牽一髮而動全身。為了更好地描述物件之間存在的這種一對多的聯動,觀察者模式應運而生。

觀察者模式(Chain of Responsibility) 學習難度:★★★☆☆ 使用頻率:★★★★★

一、多人聯機對戰遊戲的設計

需求背景:M公司欲開發一款多人聯機對戰遊戲,在遊戲中,多個遊戲玩家可以加入同一戰隊組成聯盟,當戰隊中某一成員收到敵人攻擊時將給所有其他盟友傳送通知,盟友收到通知後將作出響應。M公司開發人員需要提供一個設計方案來實現戰隊成員之間的聯動。

  M公司開發人員通過分析,發現在該系統中戰隊成員之間的聯動過程可以簡單描述如下:

  聯盟成員收到攻擊 => 傳送通知給盟友 => 盟友作出響應

  如果每個聯盟成員都需要持有盟友的資訊才能及時通知每一位盟友,因此這樣系統開銷較大。因此,M公司開發人員決定引入一個新角色“戰隊控制中心”來負責維護和管理每個戰隊所有成員的資訊,如下圖所示:

  如何實現物件之間的聯動?讓我們來看看觀察者模式吧。

二、觀察者模式概述

2.1 觀察者模式簡介

  觀察者模式是一種使用頻率最高的設計模式之一,用於建立一種物件與物件之間的依賴關係,一個物件發生改變時將自動通知其他物件,其他物件將相應作出反應。

觀察者(Observer)模式:定義物件之間的一種一對多依賴關係,使得當每一個物件狀態發生改變時,其相關依賴物件皆得到通知並被自動更新。  

2.2 觀察者模式結構

  觀察者模式包含以下4個角色:

  (1)Subject(抽象目標):又稱為主題,是被觀察的物件。

  (2)ConcreteSubject(具體目標):抽象目標的子類,通常包含有經常發生改變的資料,當它的狀態發生改變時,向其各個觀察者發出通知。

  (3)Observer(抽象觀察者):觀察者將對觀察目標的改變做出反應。

  (4)ConcreteObserver(具體觀察者):具體觀察者中維持一個指向具體目標物件的引用,它用於儲存具體觀察者的有關狀態,這些狀態需要和具體目標地狀態保持一致。

三、重構多人聯機對戰遊戲

3.1 重構後的設計結構

  其中,AllyControlCenter充當抽象目標,ConcreteAllyControlCenter則充當具體目標,IObserver充當抽象觀察者,Player則充當具體觀察者。

3.2 具體程式碼實現

  (1)抽象觀察者:IObserver

    /// <summary>
    /// 抽象觀察類 - IObserver介面
    /// </summary>
    public interface IObserver
    {
        string Name { get; set; }
        void Help();                                                                // 宣告支援盟友的方法
        void BeAttacked(AllyControlCenter acc);     // 宣告遭受攻擊的方法
    }

  (2)具體觀察者:Player

    /// <summary>
    /// 具體觀察者類:戰隊成員
    /// </summary>
    public class Player : IObserver
    {
        public string Name
        {
            get;
            set;
        }

        public void BeAttacked(AllyControlCenter acc)
        {
            Console.WriteLine("{0}:我正被攻擊,速來援救!", this.Name);
            // 呼叫戰隊控制中心類的通知方法來通知盟友
            acc.NotifyObserver(this.Name);
        }

        public void Help()
        {
            Console.WriteLine("{0} :堅持住,立馬來救你!", this.Name);
        }
    }

  (3)抽象目標:AllyControlCenter

    /// <summary>
    /// 抽象目標類:戰隊控制中心
    /// </summary>
    public abstract class AllyControlCenter
    {
        public string AllyName { get; set; }
        protected IList<IObserver> playerList = new List<IObserver>();

        public void Join(IObserver observer)
        {
            playerList.Add(observer);
            Console.WriteLine("通知:{0} 加入 {1} 戰隊", observer.Name, this.AllyName);
        }

        public void Quit(IObserver observer)
        {
            playerList.Remove(observer);
            Console.WriteLine("通知:{0} 退出 {1} 戰隊", observer.Name, this.AllyName);
        }

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

  (4)具體目標:ConcreteAllyControlCenter

    public class ConcreteAllyControlCenter : AllyControlCenter
    {
        public ConcreteAllyControlCenter(string allyName)
        {
            Console.WriteLine("系統通知:{0} 戰隊組建成功!", this.AllyName);
            Console.WriteLine("-------------------------------------------------------");
            this.AllyName = allyName;
        }

        // 實現通知方法
        public override void NotifyObserver(string playerName)
        {
            Console.WriteLine("通知:盟友們,{0} 正遭受敵軍攻擊,速去搶救!", playerName);
            foreach (var player in playerList)
            {
                if (!player.Name.Equals(playerName, StringComparison.OrdinalIgnoreCase))
                {
                    player.Help();
                }
            }
        }
    }

  (5)客戶端測試

    public class Program
    {
        public static void Main(string[] args)
        {
            // Step1.定義觀察者物件
            AllyControlCenter acc = new ConcreteAllyControlCenter("金庸群俠");
            // Step2.定義4個觀察者物件
            IObserver playerA = new Player() { Name = "楊過" };
            acc.Join(playerA);
            IObserver playerB = new Player() { Name = "令狐沖" };
            acc.Join(playerB);
            IObserver playerC = new Player() { Name = "張無忌" };
            acc.Join(playerC);
            IObserver playerD = new Player() { Name = "段譽" };
            acc.Join(playerD);
            // Step3.當某盟友遭受攻擊
            playerA.BeAttacked(acc);

            Console.ReadKey();
        }
    }

  編譯除錯結果如下圖所示:

  

  在本例項中,實現了兩次物件之間的聯動:Player.BeAttacked() => AllyControlCenter.NotifyObserver() => Player.Help()

四、觀察者模式與MVC

  在當前流行的MVC(Model-View-Controller)結構中也應用了觀察者模式,它包含了3個角色:模型、檢視和控制器。其中,模型可對應觀察者模式中的觀察目標,而檢視則對應於觀察者,控制器充當二者之間的中介者。當模型層的資料發生改變時,檢視將會自動改變其顯示內容,如下圖所示:

五、觀察者模式總結

5.1 主要優點

  (1)可以實現表示層和資料邏輯層的分離 => 各種不同的表示層可以充當具體觀察者

  (2)支援廣播通訊,觀察目標會向已註冊的觀察者物件傳送通知 => 簡化一對多系統設計的難度

(3)增加新的觀察者無須修改原有系統程式碼 => 滿足開閉原則

5.2 主要缺點

  (1)如果一個觀察目標有很多直接和間接的觀察者 => 所有觀察者收到通知會花費大量時間

  (2)如果觀察者和觀察目標之間存在迴圈依賴 => 可能導致系統崩潰

5.3 應用場景

  (1)一個抽象模型有兩個方面,其中一個方面依賴於另一個方面 => 封裝起來使其獨立改變和複用

  (2)一個物件的改變將導致一個或多個其他物件也發生改變,但並不知道具體有多少個物件將要發生改變 => 最熟悉的陌生人

參考資料

  DesignPattern

  劉偉,《設計模式的藝術—軟體開發人員內功修煉之道》

作者:周旭龍

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。

相關推薦

設計模式征途15.觀察Observer模式

在日常生活中,交通訊號燈指揮者日益擁擠的城市交通。紅燈亮,汽車停止;綠燈亮,汽車繼續前行;在這個過程中,交通訊號燈是汽車的觀察目標,而汽車則是觀察者。隨著交通訊號燈的變化,汽車的行為也會隨之變化,一盞交通訊號燈可以指揮多輛汽車。在軟體系統中,有些物件之間也存在類似交通訊號燈和汽車之間的關係,一個物件的狀態或行

設計模式觀察Observer模式

image 強制轉換 trace vat PE sta obs observer -a 設計模式:觀察者(Observer)模式 一、前言 觀察者模式其實最好的名稱應該是“發布訂閱”模式,和我們現在大數據之中的發布訂閱方式比較類似,但是也有區別的地方,在上一個設計模式,

設計模式征途—22.中介Mediator模式

我們都用過QQ,它有兩種聊天方式:一是私聊,二是群聊。使用QQ群,一個使用者就可以向多個使用者傳送相同的資訊和檔案,從而無需一一發送,節省大量時間。通過引入群的機制,極大地減少系統中使用者之間的兩兩通訊,使用者與使用者之間的聯絡可以通過群的機制來實現。 在有些軟體中,某些類/物件之間的相互呼叫關係錯綜複

設計模式征途—6.建造Builder模式

建造者模式又稱為生成器模式,它是一種較為複雜、使用頻率也相對較低的建立型模式。建造者模式為客戶端返回的不是一個簡單的產品,而是一個由多個部件組成的複雜產品。因為,沒有人買車會只買一個方向盤或者輪胎,大家買的都是一輛包含輪胎、方向盤和發動機等多個部件組成的完整汽車。如何將這些部件組裝成一輛完整的汽車並返回給使用

【java設計模式】之 建造Builder模式

        我們還是舉上一節的例子:生產汽車。上一節我們通過模板方法模式控制汽車跑起來的動作,那麼需求是無止境的,現在如果老闆又增加了額外的需求:汽車啟動、停止、鳴笛引擎聲都由客戶自己控制,他想要什麼順序就什麼順序,那該如何做呢? 1. 汽車無休止的改造       

設計模式征途—23.解釋器Interpreter模式

args 參考資料 轉載 返回 下一個 tle title 缺點 images 雖然目前計算機編程語言有好幾百種,但有時人們還是希望用一些簡單的語言來實現特定的操作,只需要向計算機輸入一個句子或文件,就能按照預定的文法規則來對句子或文件進行解釋。例如,我們想要只輸入一個加法

設計模式之裝飾Decorator模式

首先來看一個場景,如圖: 工人分為很多種類,比如電工,管道工等等,同時又有A公司的電工,B公司的電工,A公司的管道工,B公司的管道工等等,那麼當有M個工種和N個公司的時候,就會有 M * N 個子類,這個繼承體系就會變得很龐大和複雜。那麼如何簡化呢,那麼

設計模式征途—1.單例Singleton模式

  單例模式屬於建立型模式的一種,建立型模式是一類最常用的設計模式,在軟體開發中應用非常廣泛。建立型模式將物件的建立和使用分離,在使用物件時無需關心物件的建立細節,從而降低系統的耦合度,讓設計方案更易於修改和擴充套件。每一個建立型模式都在檢視回答3個問題:3W -> 建立什麼(What)、由誰建立(Wh

設計模式征途—8.橋接Bridge模式

在現實生活中,我們常常會用到兩種或多種型別的筆,比如毛筆和蠟筆。假設我們需要大、中、小三種類型的畫筆來繪製12中不同的顏色,如果我們使用蠟筆,需要準備3*12=36支。但如果使用毛筆的話,只需要提供3種型號的毛筆,外加12個顏料盒即可,涉及的物件個數僅為3+12=15,遠遠小於36卻能實現與36支蠟筆同樣的功

設計模式征途—12.享元Flyweight模式

現在在大力推行節約型社會,“浪費可恥,節儉光榮”。在軟體系統中,有時候也會存在資源浪費的情況,例如,在計算機記憶體中儲存了多個完全相同或者非常相似的物件,如果這些物件的數量太多將導致系統執行代價過高。那麼,是否存在一種技術可以用於節約記憶體使用空間,實現對這些相同或者相似物件的共享訪問呢?答案是肯定的,這種技

設計模式】module模式&&Revealing module 揭示模式

但是 出版 參數傳遞 9.png 自然 指向 們的 private 初級 寫在前面 《head first設計模式》裏有一篇文章,是說使用模式的心智,   1、初學者"心智" :"我要為HELLO WORLD找個模式"   2、中級人員模式: "或許這裏我需要一個單件

【java設計模式】之 單例Singleton模式

1. 單例模式的定義         單例模式(Singleton Pattern)是一個比較簡單的模式,其原始定義如下:Ensure a class has only one instance, and provide a global point of access

內容提供者ContentProvider和內容觀察ContentObserver的使用以應用鎖為例

內容提供者(ContentProvider) package com.songyan.applock; import android.content.ContentProvider; import android.content.ContentValues; impor

程式設計模式(十二) C++ 代理Proxy模式

2.7 Proxy 代理模式為其他物件提供一種代理以控制對這個物件的訪問。 在需要用比較通用和複雜的物件指標代替簡單的的指標的時候,使用代理模式。有四種常用的情況:        1、遠端代理,也就是為一個物件在不同的地址空間提供區域性代表。這樣可以隱藏一個物件存在於不

GOF23設計模式觀察模式observer

hang 事件監聽器 rgs str arr public pda import lob 一、觀察者模式概述   觀察者模式主要用於 1 :N 的通知。當一個對象(目標對象 Subject 或 Observable)的狀態變化時,它需要通知一系列對象(觀察者對象 Obser

設計模式 ( 十五 ) 觀察模式Observer物件行為型

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

設計模式-觀察模式Observer

觀察者模式是行為模式的一種,它的作用是當一個物件的狀態發生變化時,能夠自動通知關聯物件,自動重新整理物件狀態。 觀察者模式提供給關聯物件一種同步通訊的手段,使某個物件與依賴它的其他物件之間保持狀態同步。 角色和職責: 1.被觀察者(Observable)-:    被觀察者物件,當

設計模式學習之觀察模式Observer

a、效能降低。       在許多實現中,觀察器的 update() 方法可能與主體在同一執行緒中執行。如果觀察器列表很長,則執行 Notify() 方法可能需要很長時間。抽取物件依賴性並不意味著新增觀察器對應用程式沒有任何影響。 b、記憶體洩漏。        在 Observer 中使用的回撥機制(當物

深入淺出設計模式python版——觀察模式observer

書中第二個設計模式——觀察者模式 首先,祭出類圖 書中舉得例子: 一般方式 使用java內建模組的方式 python程式碼: 主題/可觀察者: # -*- coding:utf-8 -*- #Subject.py import Observer class

設計模式之-觀察模式Observer

              觀察者模式定義物件間的一種一對多的依賴關係,以便當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並自動重新整理。 何時使用 • 當一個抽象模型有兩個方面, 其中一個方面依賴於另一方面。將這二者封裝在獨立的對 象中以使它們可以各自獨立地改