1. 程式人生 > >設計模式 深入淺出之——觀察者模式

設計模式 深入淺出之——觀察者模式

觀察者模式

當物件間存在一對多關係時,則使用觀察者模式(Observer Pattern)。比如,當一個物件被修改時,則會自動通知它的依賴物件。觀察者模式屬於行為型模式。

介紹

意圖:定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。

主要解決:一個物件狀態改變給其他物件通知的問題,而且要考慮到易用和低耦合,保證高度的協作。

何時使用:一個物件(目標物件)的狀態發生改變,所有的依賴物件(觀察者物件)都將得到通知,進行廣播通知。

如何解決:使用面向物件技術,可以將這種依賴關係弱化。

關鍵程式碼:在抽象類裡有一個 ArrayList 存放觀察者們。

應用例項:

 1、拍賣的時候,拍賣師觀察最高標價,然後通知給其他競價者競價。 2、西遊記裡面悟空請求菩薩降服紅孩兒,菩薩灑了一地水招來一個老烏龜,這個烏龜就是觀察者,他觀察菩薩灑水這個動作。

優點: 1、觀察者和被觀察者是抽象耦合的。 2、建立一套觸發機制。

缺點: 1、如果一個被觀察者物件有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。 2、如果在觀察者和觀察目標之間有迴圈依賴的話,觀察目標會觸發它們之間進行迴圈呼叫,可能導致系統崩潰。 3、觀察者模式沒有相應的機制讓觀察者知道所觀察的目標物件是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。

實現

觀察者模式使用三個類 Subject、Observer 和 Client。Subject 物件帶有繫結觀察者到 Client 物件和從 Client 物件解綁觀察者的方法。我們建立 Subject

 類、Observer 抽象類和擴充套件了抽象類 Observer 的實體類。

ObserverPatternDemo,我們的演示類使用 Subject 和實體類物件來演示觀察者模式。

觀察者模式的 UML 圖

模式一:介面的形式


#region 模組資訊
// **********************************************************************
// Copyright (C) 2018 Blazors
// Please contact me if you have any questions
// File Name:             ErrorAbout
// Author:                romantic123fly
// WeChat||QQ:            at853394528 || 853394528 
// **********************************************************************
#endregion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ErrorAbout{

    /// <summary>  
    /// 訂閱者介面的集合  
    /// </summary>  
    private List<IHandle> handles = new List<IHandle>();

    /// <summary>  
    /// 在主模組中可以通過此方法向 訂閱者介面的集合 中新增新的訂閱者(具體處理錯誤的方法)  
    /// </summary>  
    /// <param name="handle"></param>  
    public void AddErrorHanding(IHandle handle)
    {
        handles.Add(handle);
    }

    /// <summary>  
    /// 錯誤處理的方法,主模組中通過呼叫此方法來觸發一系列的錯誤處理  
    /// </summary>  
    public void ErrorHanding()
    {
        //遍歷訂閱者介面的集合  
        foreach (var handle in handles)
        {
            //執行集合中的每個錯誤處理的方法  
            handle.ErrorHanding();
        }
    }
}

介面

#region 模組資訊
// **********************************************************************
// Copyright (C) 2018 Blazors
// Please contact me if you have any questions
// File Name:             Ihandle
// Author:                romantic123fly
// WeChat||QQ:            at853394528 || 853394528 
// **********************************************************************
#endregion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface IHandle{
    void ErrorHanding();
}

實現介面類

#region 模組資訊
// **********************************************************************
// Copyright (C) 2018 Blazors
// Please contact me if you have any questions
// File Name:             Handle
// Author:                romantic123fly
// WeChat||QQ:            at853394528 || 853394528 
// **********************************************************************
#endregion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HandleA : IHandle
{
    public void ErrorHanding()
    {
        Debug.Log("出現錯誤!傳送了一封郵件到管理員a");
    }
}
public class HandleB : IHandle
{
    public void ErrorHanding()
    {
        Debug.Log("出現錯誤!傳送了一封郵件到管理員b");

    }
}
public class HandleC : IHandle
{
    public void ErrorHanding()
    {
        Debug.Log("出現錯誤!傳送了一封郵件到管理員c");

    }
}

呼叫

#region 模組資訊
// **********************************************************************
// Copyright (C) 2018 Blazors
// Please contact me if you have any questions
// File Name:             Main
// Author:                romantic123fly
// WeChat||QQ:            at853394528 || 853394528 
// **********************************************************************
#endregion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*意圖:定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。
主要解決:一個物件狀態改變給其他物件通知的問題,而且要考慮到易用和低耦合,保證高度的協作。
何時使用:一個物件(目標物件)的狀態發生改變,所有的依賴物件(觀察者物件)都將得到通知,進行廣播通知。
如何解決:使用面向物件技術,可以將這種依賴關係弱化。
關鍵程式碼:在釋出者類裡有一個 List 存放觀察者們。
優點: 1、觀察者和被觀察者是抽象耦合的。 2、建立一套觸發機制。
缺點: 1、如果一個被觀察者物件有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
2、如果在觀察者和觀察目標之間有迴圈依賴的話,觀察目標會觸發它們之間進行迴圈呼叫,可能導致系統崩潰。
3、觀察者模式沒有相應的機制讓觀察者知道所觀察的目標物件是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。
*/
public class Main : MonoBehaviour {

	// Use this for initialization
	void Start () {
        //假設在這個位置,系統出現了錯誤,需要進行錯誤處理  
        Debug.Log("系統出現了嚴重錯誤,需要進行一些處理~~~");

        //例項化錯誤處理相關的類(釋出者)  
        ErrorAbout errorAbout = new ErrorAbout();
        //向釋出者新增訂閱者  
        errorAbout.AddErrorHanding(new HandleA());
        errorAbout.AddErrorHanding(new HandleB());
        errorAbout.AddErrorHanding(new HandleC());
        //只要釋出者的方法一執行,所有訂閱者的方法也都會被執行  
        errorAbout.ErrorHanding();

    }
}

模式二:委託的形式

1.建立觀察者,定義委託

#region 模組資訊
// **********************************************************************
// Copyright (C) 2018 Blazors
// Please contact me if you have any questions
// File Name:             ErrorAbout
// Author:                romantic123fly
// WeChat||QQ:            at853394528 || 853394528 
// **********************************************************************
#endregion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public delegate void ErrorHandle();
namespace ObserverMode
{
    public class ErrorAbout
    {
        //定義一個 具體錯誤處理方法委託 的變數  
        private ErrorHandle errorHandle;

        //向外界提供一個可以向內部委託變數新增的方法  
        public void AddErrorHanding(ErrorHandle errorHandle)
        {
            //將傳進來的方法加入委託變數中  
            if (this.errorHandle == null)
            {
                this.errorHandle = new ErrorHandle(errorHandle);
            }
            else
            {
                this.errorHandle += new ErrorHandle(errorHandle);
            }
        }

        /// <summary>  
        /// 錯誤處理的方法,主模組中通過呼叫此方法來觸發一系列的錯誤處理  
        /// </summary>  
        public void ErrorHanding()
        {
            //呼叫委託,相當於呼叫了委託中的所有方法  
            errorHandle();
        }
    }
}
#region 模組資訊
// **********************************************************************
// Copyright (C) 2018 Blazors
// Please contact me if you have any questions
// File Name:             Handle
// Author:                romantic123fly
// WeChat||QQ:            at853394528 || 853394528 
// **********************************************************************
#endregion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ObserverMode
{
    public class Handle1
    {
        public void ErrorHanding()
        {
            Debug.Log("出現錯誤!傳送了一封郵件到管理員a!");
        }
    }
    public class Handle2
    {
        public void ErrorHanding()
        {
            Debug.Log("出現錯誤!傳送了一封郵件到管理員b!");
        }
    }
    public class Handle3
    {
        public void ErrorHanding()
        {
            Debug.Log("出現錯誤!傳送了一封郵件到管理員c!");
        }
    }
}
#region 模組資訊
// **********************************************************************
// Copyright (C) 2018 Blazors
// Please contact me if you have any questions
// File Name:             Main
// Author:                romantic123fly
// WeChat||QQ:            at853394528 || 853394528 
// **********************************************************************
#endregion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ObserverMode
{
    public class Main : MonoBehaviour
    {
        void Start()
        {
            //假設在這個位置,系統出現了錯誤,需要進行錯誤處理  
            Debug.Log("系統出現了嚴重錯誤,需要進行一些處理~~~");

            //例項化錯誤處理相關的類(釋出者)  
            ErrorAbout errorAbout = new ErrorAbout();
            //向釋出者新增訂閱者  
            Handle1 handle1 = new Handle1();
            errorAbout.AddErrorHanding(handle1.ErrorHanding);
            Handle2 handle2 = new Handle2();
            errorAbout.AddErrorHanding(handle2.ErrorHanding);
            Handle3 handle3 = new Handle3();
            errorAbout.AddErrorHanding(handle3.ErrorHanding);
            //只要釋出者的方法一執行,所有訂閱者的方法也都會被執行  
            errorAbout.ErrorHanding();
        }
    }
}