欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

用学校为例聊聊监听器模式的演变,看丑小鸭怎么变成天鹅的 监听器观察者设计模式 

程序员文章站 2022-05-03 12:00:02
...

       先介绍下监听器模式。它的学名叫做观察者模式,因为观察者一般都取名为listener,所以越来越多的人称它为监听器模式。它的主要作用有2个,一是扩展,比如web容器的listener,就是在web.xml中配置的那种,因为web容器(比如tomcat)无法预料到开发者在启动、销毁或者其它一些事件中全部行为,所以就预留了一些listener接口;另一种作用是解耦,比如领域驱动设计(DDD)中,常将监听器和CQRS(读写分离)结合来处理业务逻辑。以提交订单为例,提交订单不仅要保存订单信息,还要修改库存,发货,短信通知等等。把他们都合在一个流程里,就会使流程臃肿,引入监听器模式,我们就能在提交订单的时候,发布一个订单事件,由各个listener自己去监听,发短信的listener监听到下单后就发短信,发货listener监听到下单后,就做发货动作。每个listener做的事情都很单一,耦合度很低。

     现在让我们来看看观察者模式是怎么演变得来的。比如有个类student,然后它有个方法叫goTo(dest),dest是目标。上课铃响了,我们想让学生去教室,直接调student.goTo(教室)就行了。然后发现只有学生去教室还不行,也得让老师去教室。所以上课的时候,也要调用teacher.goTo(教室)。接着,我们发现可以提取出People接口,因为是人,都可以去一个地方。


用学校为例聊聊监听器模式的演变,看丑小鸭怎么变成天鹅的
            
    
    
        监听器观察者设计模式 
 

我们在上课类里,建了个peoples列表,存储了2个people的实例:student和teacher。在上课的时候,循环调用其中的goto方法。

List<People> peoples=new ArrayList();
static{
     peoples.add(new Student());
     peoples.add(new Teacher());
}
上课(){
       for(People p:peoples){
              p.goto(教室);
     }
}

 

     很快,我们会发现这种方式很麻烦。比如要做早操了,在早操类里也要有上述peoples的初始化。这里我们可以引入单例模式,把peoples抽出来,放到一个第三方对象里,实例代码如下:

 

class PeoplesHolder{
     private static PeoplesHolder holder=new holder();
     private List<People> peoples;
     public static PeoplesHolder getHolder(){
     }

     PeoplesHolder(){
           List<People> peoples=new ArrayList();
           peoples.add(new Student());
           peoples.add(new Teacher());
     }
     
     public void goTo(dest){
         for(People p:peoples){
             p.goto(dest);
         }
     }        
}
 现在,上课的时候,只要调用PeoplesHolder.getHolder().goTo(教室)。做操的时候,只要调用PeoplesHolder.getHolder().goTo(操场)就可以了。使用比之前简单许多了,代码也就优美许多了。
       但我们会很快的发现,这还是不够的。因为people除了走路之外,还有很多其他的活动,比如放学了,要去吃饭。逻辑与走路类似,我们会发现PeoplesHolder还需要另一个方法,PeoplesHolder.getHolder().eat()。接着,我们发现走路的peoples成员与吃饭不会完全一致,比如校长不需要去上课,但他也要去吃饭。没办法,我们只好再建一个PeoplesHolder2。也就是用PeoplesHolder2.getHolder().eat()来完成吃放的动作。
    接下来,我们发现问题大了。因为people除了走路跟吃饭外,还有很多其他可能的操作。造成的结果是PeoplesHolder的种类不断膨胀,所以也要像刚才抽取PeoplesHolder那样,再将goTO、eat之类的动作给抽象出来。联系到实际业务,我们发现,走路和吃饭都有先决条件的,比如上课铃声,开饭时间等。也就是我们要观察某个条件才行。所以people接口修改如下:
interface People{
       观察(条件)
}
 以上的观察、条件是中国人的说法,外国人更喜欢用listen。也就是看和听的区别,所以观察者模式也被称为监听器模式。而条件编程语言里经常称为事件,也就是event。
interface People{
       listen(event)
}
到现在,监听器模式总算成型了。根据开闭原则,我们将listen的操作独立出来,做成新的接口PeopleListener;另外把Holder类改名为Publisher。就演变成如下结构:
interface PeopleListener{
       listen(Event event)
}

class WalkListener implements PeopleListener{
     private People;
}
class EatListener implements PeopleListener{
    private People;
}

Class Event{
}

class EventPublisher{
     private static EventPublisher holder=new EventPublisher();
     private List<PeopleListener> listeners;
     public static EventPublisher getHolder(){
     }

     PeoplesHolder(){
           List<PeopleListener> listeners=new ArrayList<PeopleListener>();
          ....
     }
     
     public void publish(event){
         for(People p:peoples){
             p.listen(event);
         }
     }        
}
 
现在,无论吃饭、睡觉还是走路,都只要调用EventPublisher.getHolder().pubulish(event)就行,而event是不同的,每个操作都要有一种event。所有的listener都能监听到该事件,然后自己判断需不需要做出反应。比如WalkListener,它只有在event类型时走路的时候,它才做出反应。
    到此,监听器模式就成型了。所有的设计模式,都是从代码的提炼中得来的。它也不会是固定格式的,会随着应用而不断演变。PS:People是人类,似乎Person更合适点。
 
  • 用学校为例聊聊监听器模式的演变,看丑小鸭怎么变成天鹅的
            
    
    
        监听器观察者设计模式 
  • 大小: 8.3 KB