观察者模式

1. 双向耦合的代码

class Program
{
    static void Main(string[] args)
    {

        //前台小姐童子喆
        Secretary tongzizhe = new Secretary();
        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏关姹", tongzizhe);
        StockObserver tongshi2 = new StockObserver("易管查", tongzizhe);

        //前台记下了两位同事
        tongzizhe.Attach(tongshi1);
        tongzizhe.Attach(tongshi2);
        //发现老板回来
        tongzizhe.SecretaryAction = "老板回来了!";
        //通知两个同事
        tongzizhe.Notify();

        Console.Read();
    }
}

//前台秘书类
class Secretary
{
    //同事列表
    private IList<StockObserver> observers = new List<StockObserver>();
    private string action;

    //增加
    public void Attach(StockObserver observer)
    {
        observers.Add(observer);
    }

    //减少
    public void Detach(StockObserver observer)
    {
        observers.Remove(observer);
    }

    //通知
    public void Notify()
    {
        foreach (StockObserver o in observers)
            o.Update();
    }

    //前台状态
    public string SecretaryAction
    {
        get { return action; }
        set { action = value; }
    }

}

//看股票的同事
class StockObserver
{
    private string name;
    private Secretary sub;

    public StockObserver(string name, Secretary sub)
    {
        this.name = name;
        this.sub = sub;
    }

    public void Update()
    {
        Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SecretaryAction, name);
    }
}

A: 前台类和这个看股票者类之间怎么样?
B: 互相耦合。
A: 如果还有人相关NBA的网上直播,你的前台类代码怎么办?
B: 首先开放-封闭原则,修改原有代码就说明书设计不够好。其次是依赖倒转原则,我们应该让程序都依赖抽象,而不是互相依赖。

2. 解耦实践一

class Program
{
    static void Main(string[] args)
    {
        //前台小姐童子喆
        Secretary tongzizhe = new Secretary();
        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏关姹", tongzizhe);
        //看NBA的同事
        NBAObserver tongshi2 = new NBAObserver("易管查", tongzizhe);

        //前台记下了两位同事
        tongzizhe.Attach(tongshi1);
        tongzizhe.Attach(tongshi2);
        //发现老板回来
        tongzizhe.SecretaryAction = "老板回来了!";
        //通知两个同事
        tongzizhe.Notify();


        Console.Read();
    }
}

//前台秘书类
class Secretary
{
    //同事列表
    private IList<Observer> observers = new List<Observer>();
    private string action;

    //增加
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }

    //减少
    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }

    //通知
    public void Notify()
    {
        foreach (Observer o in observers)
            o.Update();
    }

    //前台状态
    public string SecretaryAction
    {
        get { return action; }
        set { action = value; }
    }

}

//抽象观察者
abstract class Observer
{
    protected string name;
    protected Secretary sub;

    public Observer(string name, Secretary sub)
    {
        this.name = name;
        this.sub = sub;
    }

    public abstract void Update();
}


//看股票的同事
class StockObserver : Observer
{
    public StockObserver(string name, Secretary sub)
        : base(name, sub)
    {
    }

    public override void Update()
    {
        Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SecretaryAction, name);
    }
}

//看NBA的同事
class NBAObserver : Observer
{
    public NBAObserver(string name, Secretary sub)
        : base(name, sub)
    {
    }

    public override void Update()
    {
        Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SecretaryAction, name);
    }
}

把所有的与具体观察者耦合的地方都改成了'抽象观察者'。
A: 在具体观察者中,有没有与具体的类耦合的。
B: 你的意思是'前提秘书'是一个具体的类,也应该抽象出来。
A: 你们的老板回来,前台来不及电话了,于是通知大家的任务变成谁来做。
B: 是老板,其实老板和前台都是具体的通知者,这里观察者也不应该依赖具体实现,而是一个抽象的通知者。
A: 甲与前台有矛盾,于是不再通知甲,她是否应该把这个对象从她加入的观察者列表中删除?

3. 解耦实践二

class Program
{
    static void Main(string[] args)
    {
        //老板胡汉三
        Boss huhansan = new Boss();

        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏关姹", huhansan);
        //看NBA的同事
        NBAObserver tongshi2 = new NBAObserver("易管查", huhansan);

        huhansan.Attach(tongshi1);
        huhansan.Attach(tongshi2);

        huhansan.Detach(tongshi1);

        //老板回来
        huhansan.SubjectState = "我胡汉三回来了!";
        //发出通知
        huhansan.Notify();

        Console.Read();
    }
}

//通知者接口
interface Subject
{
    void Attach(Observer observer);
    void Detach(Observer observer);
    void Notify();
    string SubjectState
    {
        get;
        set;
    }
}

class Secretary : Subject
{
    //同事列表
    private IList<Observer> observers = new List<Observer>();
    private string action;

    //增加
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }

    //减少
    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }

    //通知
    public void Notify()
    {
        foreach (Observer o in observers)
            o.Update();
    }

    //前台状态
    public string SubjectState
    {
        get { return action; }
        set { action = value; }
    }
}

class Boss : Subject
{
    //同事列表
    private IList<Observer> observers = new List<Observer>();
    private string action;

    //增加
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }

    //减少
    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }

    //通知
    public void Notify()
    {
        foreach (Observer o in observers)
            o.Update();
    }

    //老板状态
    public string SubjectState
    {
        get { return action; }
        set { action = value; }
    }
}

//抽象观察者
abstract class Observer
{
    protected string name;
    protected Subject sub;

    public Observer(string name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }

    public abstract void Update();
}

//看股票的同事
class StockObserver : Observer
{
    public StockObserver(string name, Subject sub)
        : base(name, sub)
    {
    }

    public override void Update()
    {
        Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SubjectState, name);
    }
}

//看NBA的同事
class NBAObserver : Observer
{
    public NBAObserver(string name, Subject sub)
        : base(name, sub)
    {
    }

    public override void Update()
    {
        Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SubjectState, name);
    }
}

代码结构图:
图1

4. 观察者模式

定义一种 一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

图2

class Program
{
    static void Main(string[] args)
    {
        ConcreteSubject s = new ConcreteSubject();

        s.Attach(new ConcreteObserver(s, "X"));
        s.Attach(new ConcreteObserver(s, "Y"));
        s.Attach(new ConcreteObserver(s, "Z"));

        s.SubjectState = "ABC";
        s.Notify();

        Console.Read();

    }
}

抽象通知者,一般用一个抽象类或者一个接口实现。它把所有的引用保存在一个聚集里,每个通知者可以有任何数量的观察者。

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 (Observer o in observers)
        {
            o.Update();
        }
    }
}

//具体通知者
class ConcreteSubject : Subject
{
    private string subjectState;

    //具体通知者状态
    public string SubjectState
    {
        get { return subjectState; }
        set { subjectState = value; }
    }
}

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

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

class ConcreteObserver : Observer
{
    private string name;
    private string observerState;
    private ConcreteSubject subject;

    public ConcreteObserver(
        ConcreteSubject subject, string name)
    {
        this.subject = subject;
        this.name = name;
    }
    //更新
    public override void Update()
    {
        observerState = subject.SubjectState;
        Console.WriteLine("观察者{0}的新状态是{1}",
            name, observerState);
    }

    public ConcreteSubject Subject
    {
        get { return subject; }
        set { subject = value; }
    }
}

5. 观察者模式的特点

B: 观察者模式的动机是什么?
A: 将一个系统分割成一系列互相协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为维持一致性而使各类紧密耦合,这样会给维护,扩展和重用带来不便。一旦Subject的状态发生变化,所有的Obsever都可以得到通知。Subject发出通知时并不知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体者不知道也不需要知道其他观察者的存在。
B: 当一个对象的改变需要同时改变其他对象的时候。
A: 而且当不知道具体有多少个对象有待改变时,应该考虑用观察者模式。
B: 当一个对象模型有两个方面,其中以方面依赖于另以方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立的改变和复用。
A: 总的来讲,观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
B: 这实在是依赖倒转原则的最佳体现。
A: 具体的观察者完全有可能是风马牛不相及的类,但它们都需要根据通知者的通知来做出update的操作,所有让它们都实现一个接口。也就是说,抽象观察者,代码可以是抽象的,还可以是接口。

6. 抽象观察者的不足

A: 尽管当点击'运行'按钮,确实是在通知相关的控件发生变化,但是它们是不可能用接口的方式来实现观察者模式的。因为这些控件都早已被它们的制造商给封装了。
A: 尽管已经用了抽象观察者这样的接口,但是'抽象通知者'还是依赖'抽象观察者',也就是说,万一没有了抽象观察者这样的接口,我这通知的功能就完成不了了。另外就是每个具体观察者,它不一定是'更新'的方法调用啊,就像刚才说的,我希望的是'工具箱'是隐藏,'自动窗口'是打开,这根本不是同名的方法。这应该是不足的地方吧。
B: 是呀,如果通知者和观察者之间根本就互相不知道,有客户端来决定通知谁,那就好了。

7. 事件委托实现

去掉父类观察者,所有不上一些代码,并将'更新'方法名改成各自适合的方法名。

class Program
{
    static void Main(string[] args)
    {
        //老板胡汉三
        Boss huhansan = new Boss();

        //看股票的同事
        StockObserver tongshi1 = new StockObserver("魏关姹", huhansan);
        //看NBA的同事
        NBAObserver tongshi2 = new NBAObserver("易管查", huhansan);

        huhansan.Update += new EventHandler(tongshi1.CloseStockMarket);  //----
        huhansan.Update += new EventHandler(tongshi2.CloseNBADirectSeeding);

        //老板回来
        huhansan.SubjectState = "我胡汉三回来了!";
        //发出通知
        huhansan.Notify();

        Console.Read();


    }
}

通知者接口,抽象通知者不希望依赖抽象观察者,所有增加和减少的方法也就没必要存在了。(抽象观察者以及不存在了)

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

//事件处理程序的委托
delegate void EventHandler();

class Secretary : Subject
{
    //声明一事件Update,类型为委托EventHandler
    public event EventHandler Update;

    private string action;

    public void Notify()
    {
        Update();
    }
    public string SubjectState
    {
        get { return action; }
        set { action = value; }
    }
}

class Boss : Subject
{
    //声明一事件Update,类型为委托EventHandler
    public event EventHandler Update;

    private string action;

    public void Notify()
    {
        Update();
    }
    public string SubjectState
    {
        get { return action; }
        set { action = value; }
    }
}
//看股票的同事
class StockObserver
{
    private string name;
    private Subject sub;
    public StockObserver(string name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }

    //关闭股票行情
    public void CloseStockMarket()
    {
        Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SubjectState, name);
    }
}

//看NBA的同事
class NBAObserver
{
    private string name;
    private Subject sub;
    public NBAObserver(string name, Subject sub)
    {
        this.name = name;
        this.sub = sub;
    }

    //关闭NBA直播
    public void CloseNBADirectSeeding()
    {
        Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SubjectState, name);
    }
}

8. 事件委托说明

A: 委托就是一种引用的方法类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看作是对函数的抽象,是函数的类,委托的实例将代表一个具体的函数。
A: 一个委托可以搭载多个方法,所有方法被依次唤醒。更重要的是,它可以使得委托对象所搭载的方法并不需要属于同一个类。
B: 本来在老板类中的增加和减少的抽象观察者集合以及通知时遍历的抽象观察者都不必要了。转到客服端来让委托搭载多个方法,这就解决了本来与抽象观察者的耦合问题。
A: 但委托也是有前提的,那就是委托对象所搭载的所有方法必须既有相同的原形和形式,也就是有相同的参数列表和返回值类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值