觀察者模式

 什麼是觀察者模式?設計模式

觀察者模式(有時又被稱爲發佈(Publish)-訂閱(Subscribe)模式、模型-視圖(View)模式、源-收聽者(Listener)模式或從屬者模式)是軟件設計模式的一種。在此種模式中,一個目標物件管理全部相依於它的觀察者物件,而且在它自己的狀態改變時主動發出通知。這一般透過呼叫各觀察者所提供的方法來實現。此種模式一般被用來實現事件處理系統。this

觀察者設計模式定義了對象間的一種一對多的依賴關係,以便一個對象的狀態發生變化時,全部依賴於它的對象都獲得通知並自動刷新。---百度百科spa

顧名思義,就是觀察某個對象和某對象被觀察的模式,觀察者Observer也是訂閱者Subscriber,也是監聽者,而主題Subject也是Publisher也是源。這種模式提供一對多的關係,多個觀察者對應1個Subject..net

咱們先來看一下簡單的例子,如下例子轉載自這裏,且稍做修改。一我的A觀察水是否煮沸,若水沸騰則關閉電源。其中,人A是Observer(觀察者),水是Subject(主題),當水沸騰時,將通知人水沸騰了。固然,現實中水不能直接跟人說「我沸騰啦……」,代碼裏面天然是要寫成一個方法的。設計

首先,有一個觀察者的類Person:code

 public class Person
    {
        public void Update(string str)
        {
            System.Console.WriteLine(str + "關電源");
        }
    }

有一個被觀察的類Water,被觀察者基本含有3個部分:server

  1. 添加觀察者,一個Subject至少對應一個Observer,在類中須要維護它本身的觀察者;
  2. 移除觀察者,若某個觀察者沒有必要再觀察它時,可將其移除;
  3. 當觀察者關心的狀態變化時,主動通知觀察者;
    public class Water
    {
        private Person _person;
        private bool _isBoiled;

        public Water()
        {
            _isBoiled = false;
        }

        public void SetBoiled()
        {
            _isBoiled = true;
            NotifyObserver();
        }

        public void AddObserver(Person person)
        {
            this._person = person;
        }

        public void RemoveObserver()
        {
            if (_person != null)
                _person = null;
        }

        public void NotifyObserver()
        {
            if (_isBoiled && _person != null)
            {
                _person.Update("水開了,");
                _isBoiled = false;
            }
        }
    }

在這個例子中只有一個觀察者person,觀察對象water的狀態。對象

    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            Water water = new Water();
            water.AddObserver(person);
            water.SetBoiled();
            System.Console.ReadLine();
        }
    }

上述例子是直接使用SetBoiled()來強制將狀態變成true,達到通知的目的,在與硬件交互時,多是自動探測溫度,當溫度達到100℃時,將_isboiled設爲true,這裏就直接強制賦值了。blog

咱們知道,觀察模式是對象間一對多的關係,以上述例子爲例,一壺水正在燒,可能A在觀察它的溫度,B在觀察它是否沸騰,可是在它的溫度變化時老是會通知全部的觀察者A、B……當水的狀態,而A/B也會相應做出一些動做來改變自身,那麼AB都將有共同的一個方法Update(),由於可能會有更多的觀察者CDEF……全部,咱們寫一個接口IObserver:繼承

    public interface IObserver
    {
        void Update(PublisherBase publisher);
    }

那麼全部的觀察者都必須實現這個更新的操做,本例中包括溫度觀察者和沸騰觀察者:

    public class TemperatureObserver : IObserver
    {
        public void Update(PublisherBase publisher)
        {
            Water water =(Water) publisher  ;
            System.Console.WriteLine("溫度:" + water.GetTemperature() + "     狀態:" + water.GetStatus().ToString());
        }
    }

 

    public class BoiledObserver:IObserver
    {
        string doSomething;
        public BoiledObserver(string doSomething)
        {
            this.doSomething = doSomething;
        }

        public void Update(PublisherBase publisher)
        {
            Water water = (Water)publisher;
            if (water.GetTemperature() >= 100)
            {
                System.Console.WriteLine("狀態:" + water.GetStatus().ToString());
                System.Console.WriteLine("BoiledObserver:" + doSomething);
            }
            else
            {
                System.Console.WriteLine("沒開");
            }
        }
    }

由於Subject有了更多的觀察者,那麼它如今就須要維護一個List來維護衆多的觀察者對象了,固然,不管對象都多少個,Subject必然有維護這些對象的增刪通知操做:

    public class SubjectBase
    {
        protected bool _isChanged;
        protected List<IObserver> _observers = new List<IObserver>();

        public SubjectBase()
        {
            _isChanged = false;
        }

        public void AddObserver(IObserver observer)
        {
            _observers.Add(observer);
        }

        public void RemoveObserver(IObserver observer)
        {
            _observers.Remove(observer);
        }

        public void NotifyObserver()
        {
            if (_isChanged)
            {
                foreach (IObserver observer in _observers)
                {
                    observer.Update( this);
                }
            }

        }
    }

被觀察者水繼承SubjectBase,因爲不能直接獲取水溫的變化,咱們人爲的提供水溫變化的方法類模仿這樣一個動做:

    public class Water:SubjectBase
    {
        private double _temperature;
        private WaterStatus _status;

        public Water()
        {
            this._temperature = 0;
            this._status = WaterStatus.Cold;
        }

        public Water(IObserver observer)
        {
            this.AddObserver(observer);
        }

        public double GetTemperature()
        {
            return _temperature;
        }

        public WaterStatus GetStatus()
        {
            return _status;
        }

        public void Change(double temperature)
        {
            _temperature = temperature;
            if (_temperature < 40)
                _status = WaterStatus.Cold;
            else if (_temperature >= 40 && _temperature < 60)
                _status = WaterStatus.Warm;
            else if (_temperature >= 60 && _temperature < 100)
                _status = WaterStatus.Hot;
            else 
                _status = WaterStatus.Boiled;
            this._isChanged = true;
            this.NotifyObserver();
        }

    }

    public enum WaterStatus
    {
        Cold,
        Warm,
        Hot,
        Boiled
    }

看一下如何調用,假設有1個溫度觀察者,2個沸騰觀察者,首先要把他們維護進觀察者列表,當水溫變化時,water將會Notify各個觀察者,而觀察者就會自動Update各自的行爲或狀態:

    class Program
    {
        static void Main(string[] args)
        {
            TemperatureObserver tempObserver = new TemperatureObserver();
            BoiledObserver boiledObserver1 = new BoiledObserver("關閉電源……");
            BoiledObserver boiledObserver2 = new BoiledObserver("繼續保溫……");
            Water water = new Water(tempObserver);
            water.AddObserver(boiledObserver1);
            water.AddObserver(boiledObserver2);
            water.Change(45);
            System.Console.WriteLine();
            water.Change(80);
            System.Console.WriteLine();
            water.Change(100);
            System.Console.WriteLine();
            System.Console.ReadLine();
        }
    }

運行的結果:

能夠看得出來,每次Change溫度以後,water會通知全部的觀察者。固然,在實際應用中,更常見的一個例子是,當後臺數據更新時,將自動更新前臺界面上的數據,這時候,前臺界面是觀察者,後臺數據是被觀察者。

相關文章
相關標籤/搜索