zoukankan      html  css  js  c++  java
  • 不一样的设计模式———观察者模式

    前言

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

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

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

    模型如下:

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

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

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

  • 相关阅读:
    tudou(土豆)、youku(优酷)API(有相应的dll [C#])
    创新工场2012笔试编程捕鱼和分鱼
    区别Web文本框和HTML文本框的RandOnly、Disabled
    DataRow[]、List<DataRow>无法绑定到GridView的问题解决!
    1、NHibernate入门
    Verilog HDL 学习笔记2blocking and nonblocking assignment
    Verilog HDL 学习笔记1data type
    2006.08.21网摘
    推荐十一个很酷的实用网站
    类 ObjectOutputStream的writeObject()方法的中英文对照
  • 原文地址:https://www.cnblogs.com/aoximin/p/13726813.html
Copyright © 2011-2022 走看看