概述
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。[GOF 《设计模式》]

利用事件和委托来实现Observer模式我认为更加的简单和优雅,也是一种更好的解决方案。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public abstract class Stock
    {
        public Action<object> NotifyEvent;//利用 委托与事件 实现观察者模式

        private string _symbol;
        private double _price;
        public Stock(string symbol, double price)
        {
            this.Symbol = symbol;
            this.Price = price;
        }

        private List<IObserver> observers = new List<IObserver>();//正常方式,维护一个观察者列表
        public string Symbol { get => _symbol; set => _symbol = value; }
        public double Price { get => _price; set => _price = value; }

        public void Update()
        {
            foreach (var item in observers)
            {
                item.SendData(this);
            }
        }
        public void UpdateWithDelegate()
        {
            OnNotifyChange();
        }

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

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

        public void OnNotifyChange()
        {
            NotifyEvent?.Invoke(this);
        }
    }
}
Stock

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    /// <summary>
    /// 投资者
    /// </summary>
    public class Investor : IObserver
    {
        private string _name;

        public string Name { get => _name; set => _name = value; }
        public Investor(string name)
        {
            this._name = name;
        }
        public void SendData(object obj)
        {
            if (obj is Stock)
            {
                Stock stock = (Stock)obj;

                Console.WriteLine("Notified {0} of {1}'s " + "change to {2:C}", _name, stock.Symbol, stock.Price);
            }
        }
    }

    public interface IObserver
    {
        void SendData(object obj);
    }
}
Investor

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    /// <summary>
    /// 微软公司
    /// </summary>
    public class Microsoft : Stock
    {
        public Microsoft(string symbol, double price) : base(symbol, price) { }
    }
}
Microsoft

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            IObserver investor1 = new Investor("Jom");
            IObserver investor2 = new Investor("TerryLee");
            Stock stock = new Microsoft("Microsoft", 120.00);
            ////利用 委托与事件 实现观察者模式
            //stock.NotifyEvent += new Action<object>(investor1.SendData);
            //stock.NotifyEvent += new Action<object>(investor2.SendData);
            //stock.UpdateWithDelegate();

            stock.AddObserver(investor1);
            stock.AddObserver(investor2);
            stock.Update();

            Console.ReadLine();
        }
    }
}
Program

推模式与拉模式
在Observer模式中同样区分推模式和拉模式,我先简单的解释一下两者的区别:推模式是当有消息时,把消息信息以参数的形式传递(推)给所有观察者,而拉模式是当有消息时,通知消息的方法本身并不带任何的参数,是由观察者自己到主体对象那儿取回(拉)消息。
推模式优点:当有消息时,所有的观察者都会直接得到全部的消息,并进行相应的处理程序,与主体对象没什么关系,两者之间的关系是一种松散耦合。
推模式缺点:第一是所有的观察者得到的消息是一样的,也许有些信息对某个观察者来说根本就用不上,也就是观察者不能“按需所取”;第二,当通知消息的参数有变化时,所有的观察者对象都要变化。
鉴于以上问题,拉模式就应运而生了,它是由观察者自己主动去取消息,需要什么信息,就可以取什么,不会像推模式那样得到所有的消息参数。
拉模式缺点:主体对象和观察者之间的耦合加强了,但是这可以通过抽象的手段使这种耦合关系减到最小。
拉模式:

using System;
using System.Collections.Generic;
namespace ConsoleApp1
{
    /// <summary>
    /// 抽象目标
    /// </summary>
    public abstract class Stock
    {
        //观察者列表
        private List<IObserver> observers = new List<IObserver>();
        public Stock(String symbol, double price)
        {
            this.Symbol = symbol;
            this.Price = price;
        }
        //通知观察者拉取数据
        public void Update()
        {
            foreach (IObserver ob in observers)
            {
                ob.SendData();//拉取模式,无参数,目标对象存放于观察者中
            }
        }
        public void AddObserver(IObserver observer)
        {
            observers.Add(observer);
        }
        public void RemoveObserver(IObserver observer)
        {
            observers.Remove(observer);
        }
        public String Symbol { get; set; }
        public double Price { get; set; }
    }
    /// <summary>
    /// 具体目标
    /// </summary>
    public class Microsoft : Stock
    {
        public Microsoft(String symbol, double price) : base(symbol, price) { }
    }
    /// <summary>
    /// 观察者接口
    /// </summary>
    public interface IObserver
    {
        void SendData();
    }
    /// <summary>
    /// 具体观察者
    /// </summary>
    public class Investor : IObserver
    {
        public string Name { get; set; }
        /// <summary>
        /// 目标对象
        /// </summary>
        public Stock Stock { get; set; }
        public Investor(string name, Stock stock)
        {
            this.Name = name;
            this.Stock = stock;
        }
        /// <summary>
        /// 拉取目标对象的数据
        /// </summary>
        public void SendData()
        {
            Console.WriteLine("Notified {0} of {1}'s " + "change to {2:C}", Name, Stock.Symbol, Stock.Price);
        }
    }
}

  使用:

#region 观察者模式-拉
Stock ms = new Microsoft("Microsoft", 120.00);
ms.AddObserver(new Investor("Jom", ms));
ms.AddObserver(new Investor("Jack", ms));
ms.Update();
#endregion 

 效果及实现要点
1.使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合。
2.目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知。目标对象对此一无所知。
3.在C#中的Event。委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象,委托是比抽象Observer接口更为松耦合的设计。
适用性
1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
2.当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
总结
通过Observer模式,把一对多对象之间的通知依赖关系的变得更为松散,大大地提高了程序的可维护性和可扩展性,也很好的符合了开放-封闭原则。

来源:http://www.cnblogs.com/Terrylee/archive/2006/10/23/Observer_Pattern.html

 

posted on 2018-06-06 08:41  邢帅杰  阅读(114)  评论(0编辑  收藏  举报