大话设计模式-观察者模式/事件委托

观察者模式

观察者模式又称发布-订阅模式,其定义了一种多对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能自动更新自己。

 


观察者模式的动机

将一个系统分割成一系列相互协作的类有一个很不好的副作用就是需要维护相关对象间的一致性。

我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。

当一个对象的改变需要同时改变其他对象而且他不知道具体有多少对象有待改变时,就应该考虑使用观察者模式。

当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使他们各自独立地改变和复用。

观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。这时依赖倒转原则的最佳体现。


观察者模式结构演示

通知者抽象类:Subject

一般用一个抽象类或者一个接口实现,把所有观察者的对象的引用保存在一个集合里,每个主题都可以有任何数量的观察者。这里抽象类提供了一个接口可以增加和删除观察者对象。

abstract class Subject{
    private IList<Observer> observers = new List<Observer>();
    public void Attach(Observer observer) => observers.Add(observer);
    public void Detach(Observer observer) => observers.Remove(observer);
    public void Notify(){
        foreach (var o in observers)
            o.Update();
    }
}

观察者抽象类:Observer

为所有具体观察者定义一个接口,在得到统治者的通知时通知自己。一般使用抽象类或者接口实现。接口中定义一个更新方法。

abstract class Observer{
    public abstract void Update();
}

具体通知者类:ConcreteSubject

将有关状态存入具体观察者对象,将变化通知给所有登记过的观察者。

class ConcreteSubject : Subject{
    public string SubjectState { get; set; }
}

具体观察者类:ConcreteObserver

实现观察者抽象类所要求的更新方法,保存一个指向具体通知者的引用,获取其变化。

class ConcreteObserver : Observer{
    private string name;
    private string observerState;
    public ConcreteSubject Subject { get; set; }
    public ConcreteObserver(ConcreteSubject subject,string name){
        Subject = subject;
        this.name = name;
    }
    public override void Update(){
        observerState = Subject.SubjectState;
        Console.WriteLine($"观察者{name}的新状态是{observerState}");           
    }
}

测试类:Program

//Main方法
ConcreteSubject s = new ConcreteSubject();
//给通知者添加三个观察者XYZ
s.Attach(new ConcreteObserver(s, "X"));
s.Attach(new ConcreteObserver(s, "Y"));
s.Attach(new ConcreteObserver(s, "Z"));
//通知者更新状态
s.SubjectState = "ABC";
//通知者通知所有订阅的观察者
s.Notify();
//测试结果
观察者X的新状态是ABC
观察者Y的新状态是ABC
观察者Z的新状态是ABC

观察者模式的不足

尽管已经使用了依赖倒转原则,但是抽象通知者依旧是依赖抽象观察者,也就是说如果没有抽象观察者这样的接口,通知的功能就无法实现。此外就是每个具体观察者不一定是更新的方法要调用。


事件委托实现

委托就是一种引用方法的类型,一旦为委托分哦欸了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看作是对函数的抽象,是函数的类,委托的实例将代表一个具体函数。

一个委托可以搭载多个方法,所有方法依次被唤起,可以使委托所搭载的方法并不需要属于同一个类。前提是委托对象所搭载的所有方法必须具有相同的原型和形式,也就是相同的参数列表和返回值类型。

打游戏/看NBA的学生类:Observer

class GameObserver{
    private string name;
    private Subject sub;
    public GameObserver(string name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }
    public void CloseGame() => Console.WriteLine($"{sub.SubjectState} {name}关闭游戏,继续写作业");
}
class NBAObserver{
    private string name;
    private Subject sub;
    public NBAObserver(string name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }
    public void CloseNBA() => Console.WriteLine($"{sub.SubjectState} {name}关闭NBA视频,继续看书");
}

通知者接口:Subject

interface Subject{
    void Notify();
    string SubjectState { get; set; }
}

事件委托:

delegate void EventHandler();

老师类:

class Teacher : Subject{
    public event EventHandler Update;
    private string action;
    public string SubjectState { get; set; }
    public void Notify() => Update();
}

测试类:Program

//数学老师
Teacher math = new Teacher();
//打游戏的小明同学
GameObserver a = new GameObserver("小明",math);
//看NBA地小杨同学
NBAObserver b = new NBAObserver("小杨", math);
//将二者方法委托到老师的更新上
math.Update += new EventHandler(a.CloseGame);
math.Update += new EventHandler(b.CloseNBA);
//老师回来了
math.SubjectState = "数学老师来教室了!";
//触发通知
math.Notify();
//测试结果
数学老师来教室了! 小明关闭游戏,继续写作业
数学老师来教室了! 小杨关闭NBA视频,继续看书

猜你喜欢

转载自www.cnblogs.com/errornull/p/10057852.html