观察者模式
前言
这个东西写用的少,框架中用的多。有一次面试中,遇到了,平时我没有用过,现在整理一下。
观察者模式解决的问题是什么?
有这样的一种场景,比如说一个对象的状态发生改变,所有的依赖对象将会得到通知。
模型如下:
就是这样一个境地,那么问题来了,这样耦合性就大,比如说我要添加一个观察者,那么改动的对象就是被监听对象。
也就是说监听对象不稳定,这里问题就非常大,一般看到这个时候呢,其实100%需要重构。因为连接线最多的地方基本上要是稳定的。
观察者模式就是来降低耦合。
正文
手写代码,没有经过验证,只是介绍思想。
上面的例子是文件切割,然后进度条反馈进度。
public class FileSplitter { string pathName; ProccessBar processbar; Public FileSplitter(string pathName) { this.pathName=pathname; } public void splitter() { //处理事务.... processbar.setvalue(xx); } }
这里有个问题,那就是ProccessBar processbar;是面向具体实现类。
这里考虑是否进度条的种类会发生变化?可能有不同的进度条。
那么可以把进度条传递进来:
public class FileSplitter { string pathName; ProccessBar processbar; Public FileSplitter(string pathName,ProccessBar processbar) { this.pathName=pathname; this.processbar=processbar; } public void splitter() { //处理事务.... processbar.setvalue(xx); } }
FileSplitter 依赖于ProccessBar 的具体实现,这个时候就有个问题,因为ProccessBar 是winform的组件,这样无法复用。
还有一个问题,那就是进度条和FileSplitter耦合了,耦合是可以的,但是这两者并不相关,他们不应该存在耦合。
是啊,文件分割和进度条他们之间没有关系啊。
这个时候可能提出那么把ProccessBar去面向接口吧,不面向具体实现类。这个时候是不完全对的,面向接口抽象是为了延迟实现,但是这两者本身就不应该耦合。
那么这个时候就开始分析ProccessBar processbar;到底是做什么的?
ProccessBar 是进度条,进度条是什么?进度条是展现给人们看的进度。
这个时候抓住一个关键点在于,对于FileSplitter来说需要的只是一个进度通知,而不是一个进度条。
interface Iprogress{ void showProgress(float value); } public class FileSplitter { string pathName; Iprogress process; Public FileSplitter(string pathName) { this.pathName=pathname; } public void splitter() { //处理事务.... if(process!=null) { process.showProgress(...); } } }
这个时候就很好了,这个时候就是带通知的文件系统,是一个整体。
这个时候还有一个问题,那就是既然是进度通知,那么通知的对象就不仅仅是一个,还有一个问题就是process如何传进来,那么可以改一下。
interface Iprogress{ void showProgress(float value); } public class FileSplitter { string pathName; List<Iprogress> processes; Public FileSplitter(string pathName,ProccessBar process=null) { this.pathName=pathname; this.processes=new List<IProgress>; } public void addProcess(Iproccess proccess) { processes.add(proccess); } public void RemoveProcess(Iproccess proccess) { processes.remove(proccess); } public void splitter() { //处理事务.... foreach(var proccess in processes) { process.showProgress(...); } } }
那么要使用ProccessBar,这样写:
class mysql:form,Iprogress { ProccessBar proccessbar; void dosomething() { FileSplitter f=new FileSplitter (); f.addProcess(this); } //具体实现 void showProgress(float value) { proccessbar.setValue(value); } }
很多人看到观察者想到了委托,很大一部门原因是List processes;,可以替换成委托。
上面的代码还有一个问题就是:
public void splitter() { //处理事务.... foreach(var proccess in processes) { process.showProgress(...); } }
违法了单一原则。
因为切割对象又做了通知,那么可以这样。
public void splitter() { //处理事务.... noticeProcesses(); } protect void noticeProcesses() { foreach(var proccess in processes) { process.showProgress(...); } }
那么这里还有一个问题,就是进度通知很多类都需要,那么如何把:
public void addProcess(Iproccess proccess) { processes.add(proccess); } public void RemoveProcess(Iproccess proccess) { processes.remove(proccess); } protect void noticeProcesses() { foreach(var proccess in processes) { process.showProgress(...); } }
这一部分移出去?很多人考虑到把进度封装成一个类,然后让具体实现类继承就可以。
观察者模式的好处:
1.无需指定观察者,通知会自动传播。
2.观察者自己实现订阅机制,而目标对象自己不知道。调用noticeProcesses即可。
还有这种实现方式,https://www.runoob.com/design-pattern/observer-pattern.html