上一篇中,我给大家详细讲解了一下工厂模式以及面试中可能会被问到的关键点,我们先来温习温习。
复习
工厂模式的关键点:
一. 工厂模式分为简单工厂,工厂和抽象工厂
二. 三种工厂的实现是越来越复杂的
三. 简单工厂通过构造时传入的标识来生产产品,不同产品都在同一个工厂中生产,这种判断会随着产品的增加而增加,给扩展和维护带来麻烦
四. 工厂模式无法解决产品族和产品等级结构的问题
五. 抽象工厂模式中,一个工厂生产多个产品,它们是一个产品族,不同的产品族的产品派生于不同的抽象产品(或产品接口)。
另外,如果面试官问你在项目中有没有用过,千万千万不要再举连接数据库的例子了,真的。。。
观察者模式
好了,温习完了以后,我们下面来讲讲今天的内容,观察者模式。
单例,工厂,观察者,我认为是设计模式三杰,为何这么说,因为这三种设计模式使用频率最高,变化最多,而且覆盖面最广,当然,也是面试中最容易被问到的。当然,像装饰,建造,策略这几个也是经常会用到的,后面文章中我会一一展开讨论。
从这里开始,我们在介绍设计模式之前,需要强调一下角色。实际上从上篇工厂模式中,就已经有角色的概念。大家想想,工厂模式中有哪些角色?没错,就是工厂角色和产品角色。同样,在观察者模式中,也有两个角色,就是观察者和被观察者。在理解设计模式的时候,首先要有个概念,就是每个角色都对应这一个类,比如观察者模式,观察者肯定对应着一个观察者类,被观察者肯定对应的被观察者类。那么设计模式实际上就是通过面向对象的特性,将这些角色解耦。
观察者模式本质上就是一种订阅/发布的模型,从逻辑上来说就是一对多的依赖关系。什么意思呢?好比是一群守卫盯着一个囚犯,只要囚犯一有异动,守卫就必须马上采取行动(也有可能是更新状态,本质上也是一种行动),那么守卫就是观察者,囚犯就是被观察者。
在一个系统中,实现这种一对多的而且之间有一定关联的逻辑的时候,由于需要保持他们之间的协同关系,所以最简便的方法是采用紧耦合,把这些对象绑定到一起。但是这样一来,一旦有扩展或者修改的时候,开发人员所面对的难度非常大,而且很容易造成 Bug。那么观察者模式就解决了这么一个问题,在保持一系列观察者和被观察者对象协同工作的同时,把之间解耦了。
好了,废话不多,撸代码:
被观察者
//被观察者
public interface IObject
{
IList<IMonitor> ListMonitor; //定义观察者集合,因为多个观察者观察一个对象,所以这里用集合
string SubjectState; //被观察者的状态
void AddMonitor(IMonitor monitor); //添加一个观察者
void RemoveMonitor(IMonitor monitor); //移除一个观察者
void SendMessage(); //向所有观察者发送消息
}
public class Subject : IObject
{
private IList<IMonitor> listMonitor = new List<IMonitor>();
public string SubjectState //被观察者的状态
public IList<IMonitor> getListMonitor() //实现具体的观察者列表属性
{
return listMonitor;
}
public IList<IMonitor> setListMonitor(IList value) //实现具体的观察者列表属性
{
listMonitor = value;
}
public void AddMonitor(IMonitor monitor) //实现具体的添加观察者方法
{
listMonitor.Add(monitor);
}
public void RemoveMonitor(IMonitor monitor) //实现具体的移除观察者方法
{
listMonitor.Remove(monitor);
}
public void SendMessage() //实现具体的发送消息方法
{
for (IMonitor m in listMonitor) //发送给所有添加过的观察者,让观察者执行update方法以同步更新自身状态
{
m.Update();
}
}
}
观察者
//观察者
public interface IMonitor //定义观察者接口
{
void Update();
}
public class Monitor : IMonitor //实现具体观察者
{
private string monitorState="Stop!"; //观察者初始状态,会随着被观察者变化而变化
private string name; //观察者名称,用于标记不同观察者
private IObject subject; //被观察者对象
public Monitor (IObject subject, string name) //在构造观察者时,传入被观察者对象,以及标识该观察者名称
{
this.subject = subject;
this.name = name;
System.out.println("我是观察者" + name",我的状态是" + monitorState);
}
public void Update() //当被观察者状态改变,观察者需要随之改变
{
monitorState = subject.SubjectState;
System.out.println("我是观察者" + name",我的状态是" + monitorState);
}
}
前端调用
//前端调用
public static void Main(string[] args)
{
IObject subject = new Subject();
subject.AddMonitor(new Monitor(subject, "Monitor_1"));
subject.AddMonitor(new Monitor(subject, "Monitor_2"));
subject.AddMonitor(new Monitor(subject, "Monitor_3"));
subject.SubjectState = "Start!";
subject.SendMessage();
}
结果如下
我是观察者Monitor_1,我的初始状态是Stop!
我是观察者Monitor_2,我的初始状态是Stop!
我是观察者Monitor_3,我的初始状态是Stop!
我是观察者Monitor_1,我的状态是Start!
我是观察者Monitor_2,我的状态是Start!
我是观察者Monitor_3,我的状态是Start!
总结
这样就完成了一个观察者模式。我们回过头来看看,在被观察者中,我定义了一个集合用来存放观察者,并且我写了一个 Add 方法一个 Remove 方法来添加和移除观察者,这体现了一对多的关系,也提供了可以控制观察者的方式。所以,我们得到第一个关键点: 每个观察者需要被保存到被观察者的集合中,并且被观察者提供添加和删除的方式。
然后我么再看一下,观察者和被观察者之间的交互活动。不难发现,我是在添加一个观察者的时候,把被观察者对象以构造函数的形式给传入了观察者。最后我让被观察者执行 sendmessage 方法,这时会触法所有观察着的 update 方法以更新状态。所以我们得到第二个关键点, 被观察者把自己传给观察者,当状态改变后,通过遍历或循环的方式逐个通知列表中的观察者。
好了,到这里你应该可以把握住观察者模式的关键了。但这里有个问题,被观察者是通过构造函数参数的形式,传给观察者的,而观察者对象时被 Add 到被观察者的 List 中。所以,我们得到第三个关键点, 虽然解耦了观察者和被观察者的依赖,让各自的变化不大影响另一方的变化,但是这种解耦并不是很彻底,没有完全解除两者之间的耦合。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于