通俗易懂设计模式解析——观察者模式

2019-10-11 15:14:13 浏览数 (1)

前言

  今天我们一起看看这个观察者模式,这个模式简单来说就是一个发布订阅类似的模式。按照名字来理解也就是存在一个观察者和一个被观察者。说几个例子给大家听,大家应该就明白了。例如在我们现在通过银行卡支付之后,会收到银行发过来的提示信息。例如当我们话费余额或者流量不足之时也会收到提示信息。这其中的逻辑帮我们理解观察者模式。当我们观察的一个对象发送变化之时就会触发某一机制。然后做出一系列的措施。

观察者模式介绍

一、来由

  在软件系统中我们经常会遇到对象之间存在一对多的关系,当一个对象被修改时,将会自动通知其依赖的对象。当然如果这些依赖关系过于紧密对于系统来说又不好抵御其变化。所以我们又需要实现其结构的松耦合。

二、意图

  定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

三、案例图

四、观察者代码示例

看上面的案例图、我们来细细说下这其中的四种角色:

抽象主题角色:将所有的观察者引用保存在一个列表,同时包含增加删除观察者的操作,包含调用抽象观察者的操作方法。

具体主题角色:实现其抽象主题的抽象方法。

抽象观察者:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

具体观察者:实现抽象观察者定义的接口,是自身的状态与主题的状态相对应。

到这里我们简单的介绍了下观察这模式的原理。我们接下来使用手机话费不足时短信提醒这一个案例来讲述观察者模式的代码示例吧,当我们手机话费低于或小于10元的时候,将会触发变化更新提示:

代码语言:javascript复制
namespace Observer_Pattern
{
    class ObserverPattern
    {
    }

    /// <summary>
    /// 抽象主题角色(将所有观察者对象的引用保存在一个列表中、含有增加和删除观察者对象操作、包含调用抽象观察者的变更操作)
    /// </summary>
    public abstract class Subject
    {
        internal List<Observer> observers ;
        internal int Balance;
        /// <summary>
        /// 保存所有观察者对象
        /// </summary>
        public Subject(int balance)
        {
            observers = new List<Observer>();
            Balance = balance;
        }

        /// <summary>
        /// 增加观察者对象
        /// </summary>
        public abstract void Adds(Observer observer);

        /// <summary>
        /// 删除观察者对象
        /// </summary>
        public abstract void Removes(Observer observer);

        /// <summary>
        /// 通知观察者变化
        /// </summary>
        public void Notice() 
        {
            foreach (var item in observers)
            {
                if (item!=null)
                {
                    item.Change(this);
                }
            }
        }
    }

    /// <summary>
    /// 抽象观察者(为所有具体观察者提供了一个当主题通知是变更自己的操作)
    /// </summary>
    public abstract class Observer
    {
        
       
        public abstract void Change(Subject Item);
    }

    /// <summary>
    /// 具体主题角色,实现抽象角色抽象方法
    /// </summary>
    public class CallChargeSubject : Subject
    {
        public CallChargeSubject( int balance) : base( balance )
        {

        }
       
        public override void Adds(Observer observer)
        {
            observers.Add(observer);
        }

       

        public override void Removes(Observer observer)
        {
            observers.Remove(observer);
        }
    }

    /// <summary>
    /// 具体观察者
    /// </summary>
    public class CallChargeObserver : Observer
    {
        internal string Name;
        public CallChargeObserver(string name)  
        {
            Name = name;
        }
        public override void Change(Subject Item)
        {
            Console.WriteLine($"{Name}您好,您当前话费余额为:{Item.Balance},为了避免后续为您带来不便请及时充值!");
        }
    }
}
代码语言:javascript复制
namespace Observer_Pattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Subject subject = new CallChargeSubject(10);
            subject.Adds(new CallChargeObserver("张三"));
            subject.Adds(new CallChargeObserver("李四"));
            subject.Notice();
        }
    }
}

使用场景及优缺点

一、使用场景

1、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用

2、当对一个对象的改变需要同时做出改变的时候,而又不知道具体有多少对象需要待改变的情况下。

3、一个对象必须通知其他对象,但是并不知道这些对象是谁。

二、优点

1、观察者和被观察者直接松耦合,依赖于抽象而不是具体实现

2、建立了一套触发的订阅发布机制。

三、缺点

1、如果一个被观察者有很多观察者的话,变化通知所有观察者需要花费很多的时间

2、观察者和被观察者之间有循环依赖的话,变化触发之后会造成循环调用。会导致系统崩溃。

3、观察者只知道被观察者发生了变化,但是并不知道被观察者是如何发生变化的。

总结

  到这里我们就介绍完了观察者模式,这个模式是一种一对多的关系依赖。如果不使用抽象主题角色和抽象观察者也是可以实现监控话费的任务的,但是这样会造成主题角色和观察者角色紧耦合。观察者模式主要实现的就是解耦代码,实现依赖倒置原则。依赖于抽象而不是具体实现。再说观察者模式、在我前面的文章又写过委托与事件,其中提到了事件基于委托,为委托提供了一种发布/订阅机制。我们仔细研究可以发现观察者模式和委托还是较为相似的。

0 人点赞