开发者社区> 愚公搬代码> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

【愚公系列】2021年12月 二十三种设计模式(十九)-观察者模式(Observer Pattern)

简介: 【愚公系列】2021年12月 二十三种设计模式(十九)-观察者模式(Observer Pattern)
+关注继续查看

文章目录

前言

一、观察者模式(Observer Pattern)

二、使用步骤

角色

示例

总结

优点

缺点

使用场景

前言

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。


提示:以下是本篇文章正文内容,下面案例可供参考


一、观察者模式(Observer Pattern)

观察者模式属于行为型模式,有时又被称为模型-视图(Model-View)模式、发布-订阅(Publish-Subscribe)模式、源-监听器(Source-Listener)模式或从属者(Dependents)模式。


观察者模式完美的将观察者和被观察的对象分离开,并在目标物件状态改变时主动向观察者发出通知(接口方法、抽象方法、委托、事件)。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。


二、使用步骤

角色

1、抽象主题(Subject)


主题需要维持对所有观察者的引用,以便在状态更改时调用观察者接口。每个主题都可以有任何数量的观察者,并可以增加和删除观察者对象;


2、具体主题(Concrete Subject)


将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有订阅过的观察者发送更改通知;


3、抽象观察者(Observer)


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


4、具体观察者(Concrete Observer)


实现抽象观察者角色所要求的通知(接收)接口,以便使本身的状态与主题状态协调。


示例

image.png

命名空间ObserverPattern中包含抽象出版社基类Publisher(主题)、中国机械工业出版社类Machine、中国农业出版社类Agriculture、读者接口IReader(观察者)、具体观察者Iori类和Jay类、图书类Book。另外为了代码更整洁,引入Extentions扩展类,方便图书和读者信息的处理。这个示例展示读者如何观察出版社发布图书的状态,并在出版社发布图书时,得到通知。

public class Book {

    public string Name { get; set; }

    public Book(string name) {
        Name = name;
    }

}

简单的图书类,仅包含一个构造函数和图书的名字属性。

public interface IReader {

    void Receive(Publisher publisher, Book book);

}

读者接口,定义公开的Receive契约,并且得到出版社和图书信息。

public class Iori : IReader {
 
    public void Receive(Publisher publisher, Book book) {
        Console.WriteLine(
            $"{this.ReaderName()} received {book.BookName()} from {publisher.Name}.");
    }
 
}
public class Jay : IReader {
 
    public void Receive(Publisher publisher, Book book) {
        Console.WriteLine(
            $"{this.ReaderName()} received {book.BookName()} from {publisher.Name}.");
    }
 
}

具体读者类,Iori和Jay,一个是我的英文名,另一个则是我的偶像。

public abstract class Publisher {
 
    private List<IReader> _readers = new List<IReader>();
 
    public string Name { get; set; }
    private const string LINE_BREAK =
        "----------------------------------------" +
        "----------------------------------------";
    //文章排版需要,故折成2行
 
    public void AttachReader(IReader reader) {
        if (reader == null) throw new ArgumentNullException();
        _readers.Add(reader);
    }
 
    public bool DetachReader(IReader reader) {
        if (reader == null) throw new ArgumentNullException();
        return _readers.Remove(reader);
    }
 
    protected virtual void OnPublish(Book book, DateTime publishTime) {
        Console.WriteLine(
            $"{Name} published {book.BookName()} at {publishTime.ToString("yyyy-MM-dd")}.");
        Console.WriteLine(LINE_BREAK);
    }
 
    public void Publish(Book book, DateTime publishTime) {
        OnPublish(book, publishTime);
        foreach (var reader in _readers) {
            if (reader != null) {
                reader.Receive(this, book);
            }
        }
        Console.WriteLine(LINE_BREAK);
    }
 
}

抽象出版社类Publisher,即被观察者,这是整个观察者模式的核心基类。首先在内部维持对IReader列表的引用,并且可以对观察者进行增加(AttachReader)或删除(DetachReader)操作。而发布方法Publish则在出版社发布新图书时,通知所有观察者。

public class Machine : Publisher {
 
    public Machine(string name) {
        Name = name;
    }
 
    protected override void OnPublish(Book book, DateTime publishTime) {
        Console.WriteLine(
            $"{Name} published {book.BookName()} at {publishTime.ToString("yyyy-MM-dd")}." +
            $"->Machine.OnPublish");
        Console.WriteLine(LINE_BREAK);
    }
 
}
public class Agriculture : Publisher {

    public Agriculture(string name) {
        Name = name;
    }

}

具体出版社类Machine和Agriculture,代表中国机械出版社和中国农业出版社。

public static class Extentions {

    public static string ReaderName(this IReader reader) {
        return reader.ToString().Replace(nameof(ObserverPattern) + ".", "");
    }

    public static string BookName(this Book book) {
        return "[" + book.Name + "]";
    }

}

公开的静态的扩展方法类,其中ReaderName扩展处理读者名称前的命名空间,使用nameof关键字是为了支持重构。BookName扩展则用来为书名加上中括号。

public class Program {

    public static void Main(string[] args) {
        Publisher publisher = new Machine("China Machine Press");

        var iori = new Iori();
        var jay = new Jay();

        publisher.AttachReader(iori);
        publisher.AttachReader(jay);

        publisher.Publish(new Book("How the Steel Was Tempered"), DateTime.UtcNow);

        publisher.DetachReader(jay);

        publisher.Publish(new Book("Jane Eyre"), DateTime.UtcNow);

        publisher = new Agriculture("China Agriculture Press");

        publisher.AttachReader(iori);
        publisher.AttachReader(jay);

        publisher.Publish(new Book("Romance of the Three Kingdoms"), DateTime.UtcNow);

        Console.ReadKey();
    }

}

这个是调用方的代码示例,首先创建机械工业出版社实例,再创建2个具体读者实例并订阅,最后出版社发布《钢铁是怎么炼成的》图书,2个读者都能收到发布通知。接下来演示了取消订阅和中国农业出版社的发布情况,请各位看官自行分析。以下是这个案例的输出结果:

China Machine Press published [How the Steel Was Tempered] at 2018-07-19.->Machine.OnPublish
--------------------------------------------------------------------------------
Iori received [How the Steel Was Tempered] from China Machine Press.
Jay received [How the Steel Was Tempered] from China Machine Press.
--------------------------------------------------------------------------------
China Machine Press published [Jane Eyre] at 2018-07-19.->Machine.OnPublish
--------------------------------------------------------------------------------
Iori received [Jane Eyre] from China Machine Press.
--------------------------------------------------------------------------------
China Agriculture Press published [Romance of the Three Kingdoms] at 2018-07-19.
--------------------------------------------------------------------------------
Iori received [Romance of the Three Kingdoms] from China Agriculture Press.
Jay received [Romance of the Three Kingdoms] from China Agriculture Press.
--------------------------------------------------------------------------------

总结

优点

1、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不了解每一个具体观察者的内部细节,它只知道它们都有一个共同的接口;

2、由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次,并且符合里氏替换原则和依赖倒置原则。


缺点

1、如果一个被观察者对象维持了较多的观察者,将所有的观察者都通知到会花费很多时间;

2、如果在被观察者之间有循环依赖的话,被观察者可能会触发它们之间进行循环调用,导致系统崩溃;

3、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。


使用场景

1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变;

2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的内部细节。


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【愚公系列】2021年12月 二十三种设计模式(二十)-状态模式(State Pattern)
【愚公系列】2021年12月 二十三种设计模式(二十)-状态模式(State Pattern)
30 0
【愚公系列】2021年12月 二十三种设计模式(十八)-备忘录模式(Memento Pattern)
【愚公系列】2021年12月 二十三种设计模式(十八)-备忘录模式(Memento Pattern)
54 0
【愚公系列】2021年12月 二十三种设计模式(十六)-迭代器模式(Iterator Pattern)
【愚公系列】2021年12月 二十三种设计模式(十六)-迭代器模式(Iterator Pattern)
39 0
设计模式---观察者模式(DesignPattern_Observer)
摘录自:设计模式与游戏完美开发 十年磨一剑,作者将设计模式理论巧妙地融入到实践中,以一个游戏的完整实现呈现设计模式的应用及经验的传承 《轩辕剑》之父——蔡明宏、资深游戏制作人——李佳泽、Product Evangelist at Unity T...
433 0
观察者模式(Observer Pattern)
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如:当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为模式。 介绍 意图: 定义对象之间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。
785 0
设计模式之观察者模式 (Observer Design Pattern)
一、什么是观察者模式 观察者定义了一种一对多的依赖关系,当一个主题(Subject)对象状态发生变化时,所有依赖它的相关对象都会得到通知并且能够自动更新自己的状态,这些依赖的对象称之为观察者(Observer)对象这类似于发布/订阅模式。 观察者模式中的主题对象,会维持着一个依赖它的观察者对象列表,当主题对象状态发生改变时,主题对象便会调用这个列表中所有观察者对象的方法。
1511 0
设计模式之观察者模式 (Observer Design Pattern)
一、什么是观察者模式 观察者定义了一种一对多的依赖关系,当一个主题(Subject)对象状态发生变化时,所有依赖它的相关对象都会得到通知并且能够自动更新自己的状态,这些依赖的对象称之为观察者(Observer)对象这类似于发布/订阅模式。 观察者模式中的主题对象,会维持着一个依赖它的观察者对象列表,当主题对象状态发生改变时,主题对象便会调用这个列表中所有观察者对象的方法。
1042 0
Net设计模式实例之观察者模式(Observer Pattern)
一、观察者模式简介(Brief Introduction) 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化的时,会通知所有观察者对象,使他们能够自动更新自己。
710 0
+关注
愚公搬代码
该博客包括:.NET、前端、IOS、Android、Linux、物联网、网络安全、python、大数据等相关使用及进阶知识。查看博客过程中,如有任何问题,皆可随时沟通。
221
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载