理解【观察者模式】和【发布订阅】的区别

一、定性区别 首先,观察者是经典软件设计模式中的一种,但发布订阅只是软件架构中的一种消息范式。所以不要再被“观察者模式和发布订阅模式xxx”这样的问题误导。 二、组成区别 其次,就是实现二者所需的角色数量有着明显的区别。观察者模式本身只需要2个角色便可成型,即观察者和被观察者,其中被观察者是重点。而发布订阅需要至少3个角色来组成,包括发布者、订阅者和发布订阅中心,其中发布订阅中心是重点。 观察者模式 发布订阅 2个角色 3个角色 重点是被观察者 重点是发布订阅中心 三、各自实现 1、观察者模式实现 观察者模式一般至少有一个可被观察的对象 Subject ,可以有多个观察者去观察这个对象。二者的关系是通过被观察者主动建立的,被观察者至少要有三个方法——添加观察者、移除观察者、通知观察者。 当被观察者将某个观察者添加到自己的观察者列表后,观察者与被观察者的关联就建立起来了。此后只要被观察者在某种时机触发通知观察者方法时,观察者即可接收到来自被观察者的消息。 上图重点表示出了被观察者通知观察者的动作,省略了观察者是如何被添加到被观察者的列表内以及接收到被观察者后是具体如何拿到通知消息的。接下来我们使用 JavaScript 来实现一版,以具体展示这部分细节。 1)被观察者对象 class Subject { constructor() { this.observerList = []; } addObserver(observer) { this.observerList.push(observer); } removeObserver(observer) { const index = this.observerList.findIndex(o => o.name === observer.name); this.observerList.splice(index, 1); } notifyObservers(message) { const observers = this.observerList; observers.forEach(observer => observer.notified(message)); } } 2)观察者 class Observer { constructor(name, subject) { this.name = name; if (subject) { subject....

2021-06-28 15:31:00 · 4 分钟 · 836 字

设计模式学习笔记(下)

继设计模式学习笔记(上) 15 抽象工厂模式 场景 不同数据库在 SQL 的具体实现上有差距,若存在系统中使用 SQL 语句上过多的使用专门的某种数据库的 SQL 语句,在迁移数据库时,会导致很多 SQL 在新的数据库中不能运行。 解决 使用原生 SQL 语句 实例 class User { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _name;; public string Name { get { return _name; } set { _name = value; } } } 模式解释 工厂方法模式是定义一个用于创建对象的接口,让子类决定实例化哪一个类 效果 由于多态的存在,声明 User 接口的对象事先不知道在访问哪个数据库,却可以在运行时正常,达到业务逻辑与数据访问解耦 抽象工厂模式 提供一个创建一系列相关或相互依赖对象的接口,而无需指定具体的类 实际使用步骤 先通过抽象工厂创建一个具体工厂,再通过具体工厂生产特定实现的产品对象...

2018-01-24 15:56:32 · 1 分钟 · 211 字

设计模式学习笔记(上)

关于 Java 设计模式的学习 继承 两个类之间是 is-a 的关系可以考虑用继承 而两个类之间是 has-a,表示某个角色具有某一项责任,不适合用继承 例如:飞机场有飞机,但是飞机不能去继承飞机场 多态 多态表示不同的对象可以执行相同的动作,但要通过它们自己的实现代码来执行 特点:(子替父演戏实例) 子类以父类身份出现 子类在工作时以自己的方式实现 子类以父类身份出现时,子类特有的属性和方法不可以使用 多态原理 当方法被调用时,无论对象是否被转换成其父类,都只有位于对象继承最末端的方法实现会被调用。 即父类的抽象方法是按照其运行时类型而非编译时类型进行动态绑定调用的 抽象类 不能实例化 抽象方法必须被子类重写 若类包含抽象方法,则类必须定义为抽象类 总结:抽象类代表一个抽象概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的。 在一个继承关系树里,树叶节点应为具体类,树枝节点均应为抽象类。 例如:动物是抽象类,猫是具体类,而当猫划分品种时,比如包含金丝猫和其他品种,那猫应该是树枝,也应是抽象类。 接口 接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。 一旦类实现了接口,类就可以支持接口所指定的所有属性和成员。 声明接口在语法上与声明抽象类完全相同,但不允许提供接口中任何成员的执行方式(没有具体实现) 所以接口不能实例化、不能有构造方法和字段、不能有修饰符、不能声明为抽象的或静态的 实现接口的类必须实现接口中所有方法和属性 区分抽象类和接口 抽象 类是对对象的抽象 抽象类是对类的抽象 接口是对行为的抽象 对象是否有相似关联 若行为跨越不同类的对象,可使用接口 对于相似的类对象,用继承抽象类 设计角度 抽象类是从子类中发现了公共的东西,泛化出父类,然后子类继承父类 接口根本不知道子类的存在,方法如何实现不确认,预先定义 通过重构改善既有代码的设计 所有抽象类往往通过重构得来 面向对象设计(OOD) 可维护 可复用 可拓展 灵活性好 UML 关联 聚合(弱拥有):A 对象可以包含 B 对象,但 B 对象不是 A 对象的一部分 组合(强拥有):严格的整体与部分的关系 依赖 简单工厂 用单独的类考虑创造实例的过程 策略模式 官方定义 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。 解释 所有的算法完成相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。 优势 另外策略模式简化了单元测试,因为每个算法有独立的类,可以通过自己的接口单独测试。...

2018-01-24 15:53:31 · 2 分钟 · 219 字

可复用面向对象软件基础——设计模式(六)之原型模式

原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。 一、原型模式(Prototype) /** * 原型类,实现可复制接口 * @author barnett * */ public class Prototype implements Cloneable { /** * 复制方法,调用父类的复制方法 */ public Object clone() throws CloneNotSupportedException { Prototype prototype = (Prototype) super.clone(); return prototype; } } 很简单,一个原型类,只需要实现Cloneable接口,覆写 clone 方法,此处 clone 方法可以改成任意的名称,因为 Cloneable 接口是个空接口,你可以任意定义实现类的方法名,如 cloneA 或者 cloneB。 因为此处的重点是 super.clone()这句话,super.clone()调用的是 Object 的 clone()方法,而在 Object 类中,clone()是 native 的。 二、深浅复制 浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。 public class PrototypeDeepAndThinClone implements Cloneable, Serializable { private static final long serialVersionUID = 1L; private String string; private SerializableObject obj; // 浅复制 public Object clone() throws CloneNotSupportedException { Prototype prototype = (Prototype) super....

2016-12-15 21:06:11 · 1 分钟 · 177 字

可复用面向对象软件基础——设计模式(五)之建造者模式

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的 Test 结合起来得到的。 代码实现 /** * 发送接口,有一个发送方法待实现 * @author barnett * */ public interface Sender { public void send(); } /** * 2、邮件发送类,实现发送接口,实现其发送方法,用以发送邮件 * @author barnett * */ public class MailSender implements Sender { @Override public void send() { System.out.println("I am MailSender!"); } } /** * 短信发送类,实现了发送接口的发送方法,用以发送短信 * @author barnett * */ public class SmsSender implements Sender { @Override public void send() { System.out.println("I am SmsSender!"); } } /** * 3、建造者类 * @author barnett * */ public class Builder { // 用以存储生产出的多个发送器 private List<Sender> list = new ArrayList<Sender>(); /** * 用于生产邮件发送器,当该方法被调用时会生产多个邮件发送器放入集合 * @param count 生产个数 */ public void produceMailSender(int count) { for(int i=0; i<count; i++) { list....

2016-12-15 20:22:46 · 1 分钟 · 164 字

可复用面向对象软件基础——设计模式(四)之单例模式

单例对象(Singleton)是一种常用的设计模式。在 Java 应用中,单例对象能保证在一个 JVM 中,该对象只有一个实例存在。 单例模式优势 某些类创建繁琐,对于一些大型对象,系统开销大 省去 new 操作符,降低系统内存使用频率,减轻 GC 压力 保证某些核心类独立控制系统整个流程(控制其不可实例多个) 简版单例类 public class Singleton { // 持有私有静态实例,防止被引用,此处赋值为null,目的为实现延迟加载 private static Singleton instance = null; // 私有构造方法,防止被实例化 private Singleton() { } // 静态工厂方法 public static Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; } // 如果该对象被用于序列化,可保证对象在序列化前后保持一致 public Object readResolve() { return instance; } } 这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?我们首先会想到对 getInstance 方法加synchronized关键字,如下: // 静态工厂方法 public static synchronized Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; } 但是,synchronized 关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用 getInstance(),都要对对象上锁。...

2016-12-15 19:57:13 · 2 分钟 · 295 字

可复用面向对象软件基础——设计模式(三)之抽象工厂模式

一、工厂方法模式缺陷 工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须修改工厂类,这违背了设计模式六大原则第一条闭包原则。 所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。 二、抽象工厂模式实现 /** * 1、邮寄接口,有一个抽象邮寄方法待实现 * @author barnett * */ public interface Sender { public void send(); } /** * 2、实现类,邮件邮寄类实现邮寄接口,实现其邮寄的方法 * @author barnett * */ public class MailSender implements Sender { @Override public void send() { System.out.println("I am MailSender!"); } } /** * 短信邮寄类实现邮寄接口,实现其邮寄方法 * @author barnett * */ public class SmsSender implements Sender { @Override public void send() { System.out.println("I am SmsSender!"); } } /** * 3、提供器接口,返回邮寄对象,其生产方法待实现 * 工厂依靠实现该接口,生产产品(Sender) * @author barnett * */ public interface Provider { public Sender produce(); } /** * 4、工厂,邮件邮寄工厂,负责专门生产邮件邮寄实例 * @author barnett * */ public class SendMailFactory implements Provider { @Override public Sender produce() { return new MailSender(); } } /** * 短信邮寄工厂,专门生产短信邮寄实例 * @author barnett * */ public class SendSmsFactory implements Provider { @Override public Sender produce() { return new SmsSender(); } } /** * 5、测试类 * @author barnett * */ public class Test { public static void main(String[] args) { // 实例一个邮件工厂(因其实现了提供器接口,可返回一个具有专门生产某种产品生产方法的实例) Provider provider = new SendMailFactory(); // 通过该实例生产产品 Sender sender = provider....

2016-12-15 16:59:43 · 1 分钟 · 188 字

可复用面向对象软件基础——设计模式(二)之工厂方法模式

一、工厂方法模式分类(3 种) 普通工厂模式 多个工厂方法模式 静态工厂方法模式 二、工厂方法模式详解 (1)普通工厂模式 普通工厂模式就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。 // 1、创建不同产品的共同接口 public interface Sender { /** * 邮寄接口中有一个邮寄方法待实现 */ public void Send(); } /** * 2、(实现类)邮件邮寄类实现了邮寄接口,实现其邮寄的抽象方法 * @author barnett */ public class MailSender implements Sender { @Override public void Send() { System.out.println("I am MailSender!"); } } /** * 短信邮寄类实现了邮寄接口,实现其邮寄的抽象方法 * @author barnett * */ public class SmsSender implements Sender { @Override public void Send() { System.out.println("I am SmsSender!"); } } /** * 3、工厂类,负责多次制造产品(new出复用的类) * @author barnett */ public class SendFactory { /** * 工厂类中的生产方法 * @param type 输入要生产的产品类型 * @return 返回一个产品 */ public Sender produce(String type) { if("mail"....

2016-12-15 12:57:58 · 2 分钟 · 319 字

可复用面向对象软件基础——设计模式(一)之总览

一、设计模式特点 反复使用 分类编目 经验总结 二、设计模式宗旨 代码重用 使代码易于理解 保证代码可靠性 三、设计模式分类(23+2) 创建型(5 种) 结构型(7 种) 行为型(11 种) 其他(2 种) (1)创建型模式 工厂方法模式 抽象工厂模式 单例模式 建造者模式 原型模式 (2)结构型模式 适配器模式 装饰器模式 代理模式 外观模式 桥接模式 组合模式 享元模式 (3)行为型模式 策略模式 模板方法模式 观察者模式 迭代子模式 责任链模式 命令模式 备忘录模式 状态模式 访问者模式 中介者模式 解释器模式 (4)其他 并发型模式 线程池模式 四、设计模式原则(6 项) 开闭原则(Open Close Principle) 里氏代换原则(Liskov Substitution Principle) 依赖倒转原则(Dependence Inversion Principle) 接口隔离原则(Interface Segregation Principle) 迪米特法则(最少知道原则)(Demeter Principle) 合成复用原则(Composite Reuse Principle) 五、原则详解 (1)开闭原则 开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。目的在于使程序的扩展性好,易于维护和升级。所以需要使用到接口和抽象类 (2)里氏代换原则 里氏代换原则(LSP)是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。...

2016-12-15 10:43:05 · 1 分钟 · 82 字