设计模式(十九)观察者

发布时间 2024-01-02 14:16:07作者: 冬先生

一、定义

定义对象之间的一种一对多依赖关系,使得当每一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式是一种行为型模式,又称为发布-订阅(Publish-Subscribe)模式、模型-视图(Model-View)模式、源-监听器(Source-Listener)模式或从属者(Dependents)模式。

二、描述

观察者模式是一种使用频率最高的设计模式之一,用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。包含以下四个角色:1、Subject(抽象目标):又称为主题,是被观察的对象。
2、ConcreteSubject(具体目标):抽象目标的子类,通常包含有经常发生改变的数据,当它的状态发生改变时,向其各个观察者发出通知。
3、Observer(抽象观察者):观察者将对观察目标的改变做出反应。
4、ConcreteObserver(具体观察者):具体观察者中维持一个指向具体目标对象的引用,它用于存储具体观察者的有关状态,这些状态需要和具体目标地状态保持一致。

三、例子

X公司欲开发一款多人联机对战游戏,在游戏中,多个游戏玩家可以加入同一战队组成联盟,当战队中某一成员收到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将作出响应。IObserver:抽象观察者

public interface IObserver
{
    string Name { get; set; }
    void Help();                                // 声明支援盟友的方法
    void BeAttacked(AllyControlCenter acc);     // 声明遭受攻击的方法
}

Player:战队成员类,充当具体观察者

public class Player : IObserver
{
    public string Name { get; set; }

    public void BeAttacked(AllyControlCenter acc)
    {
        Console.WriteLine("{0}:我正被攻击,速来援救!", this.Name);
        // 调用战队控制中心类的通知方法来通知盟友
        acc.NotifyObserver(this.Name);
    }

    public void Help()
    {
        Console.WriteLine("{0} :坚持住,立马来救你!", this.Name);
    }
}

AllyControlCenter:抽象战队控制中心类,充当抽象目标

public abstract class AllyControlCenter
{
    public string AllyName { get; set; }
    protected IList<IObserver> playerList = new List<IObserver>();

    public void Join(IObserver observer)
    {
        playerList.Add(observer);
        Console.WriteLine("通知:{0} 加入 {1} 战队", observer.Name, this.AllyName);
    }

    public void Quit(IObserver observer)
    {
        playerList.Remove(observer);
        Console.WriteLine("通知:{0} 退出 {1} 战队", observer.Name, this.AllyName);
    }

    // 声明抽象通知方法
    public abstract void NotifyObserver(string name);
}

ConcreteAllyControlCenter:具体战队控制中心类,充当具体目标

public class ConcreteAllyControlCenter : AllyControlCenter
{
    public ConcreteAllyControlCenter(string allyName)
    {
        Console.WriteLine("系统通知:{0} 战队组建成功!", this.AllyName);
        Console.WriteLine("-------------------------------------------------------");
        this.AllyName = allyName;
    }

    // 实现通知方法
    public override void NotifyObserver(string playerName)
    {
        Console.WriteLine("通知:盟友们,{0} 正遭受敌军攻击,速去抢救!", playerName);
        foreach (var player in playerList)
        {
            if (!player.Name.Equals(playerName, StringComparison.OrdinalIgnoreCase))
            {
                player.Help();
            }
        }
    }
}

Program:测试代码

// Step1.定义观察者对象
AllyControlCenter acc = new ConcreteAllyControlCenter("金庸群侠");
// Step2.定义4个观察者对象
IObserver playerA = new Player() { Name = "杨过" };
acc.Join(playerA);
IObserver playerB = new Player() { Name = "令狐冲" };
acc.Join(playerB);
IObserver playerC = new Player() { Name = "张无忌" };
acc.Join(playerC);
IObserver playerD = new Player() { Name = "段誉" };
acc.Join(playerD);
// Step3.当某盟友遭受攻击
playerA.BeAttacked(acc);
Console.ReadLine();

四、总结

1、优点

(1)观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制、并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色。
(2)在观察目标和观察者之间建立一个抽象的耦合,观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
(3)观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度。
(4)观察者模式符合开闭原则,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下增加新的观察目标也很方便。

2、缺点

(1)如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。
(2)如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们进行循环调用,可能导致系统崩溃。
(3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而只是知道观察目标发生了变化。