不一样的设计模式———观察者模式

前言

这个东西写用的少,框架中用的多。有一次面试中,遇到了,平时我没有用过,现在整理一下。

观察者模式解决的问题是什么?

有这样的一种场景,比如说一个对象的状态发生改变,所有的依赖对象将会得到通知。

模型如下:

就是这样一个境地,那么问题来了,这样耦合性就大,比如说我要添加一个观察者,那么改动的对象就是被监听对象。

也就是说监听对象不稳定,这里问题就非常大,一般看到这个时候呢,其实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

观察者模式有很多实现方式,各有优势。

posted @ 2020-09-24 21:34  敖毛毛  阅读(249)  评论(0编辑  收藏  举报