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

       先介绍下监听器模式。它的学名叫做观察者模式,因为观察者一般都取名为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更合适点。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值