不一样的设计模式———观察者模式
前言
这个东西写用的少,框架中用的多。有一次面试中,遇到了,平时我没有用过,现在整理一下。
观察者模式解决的问题是什么?
有这样的一种场景,比如说一个对象的状态发生改变,所有的依赖对象将会得到通知。
模型如下:
就是这样一个境地,那么问题来了,这样耦合性就大,比如说我要添加一个观察者,那么改动的对象就是被监听对象。
也就是说监听对象不稳定,这里问题就非常大,一般看到这个时候呢,其实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
上面的代码还有一个问题就是:
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
观察者模式有很多实现方式,各有优势。