GoF著作中未提到的设计模式(7):Publish-Subscribe

来源:互联网 发布:小欧工程师软件 编辑:程序博客网 时间:2024/06/05 02:49
很多项目中都有消息分发或者事件通知机制,尤其是模块化程度高的项目。例如在办公自动化系统中,有些子系统对于新建用户这一事件很感兴趣,权限模块希望为这个新用户设置默认的权限,报表模块希望重新生成当月的报表,诸如此类的代码都写到新建用户的业务逻辑后面,会加大耦合度,可维护性降低,并且对于每个模块都是一个独立工程的情况,这种方式更是不可取。对于简单的情形,观察者模式就适用了,如果系统中有很多地方都需要收发消息,那么它就不适用了,否则会造成类数量的膨胀,增加类的复杂性,这时候就需要一种更集中的机制,Publish-Subscribe机制是个不错的选择,它的耦合性低,各个参与者之间毫无关联。每 一个消息都有一个唯一标识,一般都用字符串来描述,比如用户管理模块中新添加了一个用户,于是它发送了一个消息:/UserManagment/User /Add,消息的其他信息可以放置到一个Map中。下面是一个最简单的消息订阅接口:

public interface MessageSubscriber {    public void onRecived(String message,Map params);}

当订阅的消息发出后,MessageSubscriber的onRecived方法就会被调用,消息相关的特定信息存储在参数params中。 一个消息可以被多个对象订阅,一个对象可以订阅多个消息,所以onRecived的第一个参数是消息的名称,如果对象订阅了多个消息,可能需要根据消息类型来进行具体的操作。
 
下面消息发送的实现类:
 public class MessagePublisher {    private static MessagePublisher singleton;    private static Map<String,ArrayList<MessageSubscriber>> subscribers;        private MessagePublisher(){}        public static MessagePublisher instance(){        if(singleton == null)            singleton = new MessagePublisher();        return singleton;    }        public void register(String message,MessageSubscriber subscriber){        if(subscriber == null)            return;        if(subscribers == null)            subscribers = new LinkedHashMap<String, ArrayList<MessageSubscriber>>();        ArrayList<MessageSubscriber> subscriberList = subscribers.get(message);        if(subscriberList == null){            subscriberList = new ArrayList<MessageSubscriber>();            subscribers.put(message, subscriberList);        }        subscriberList.add(subscriber);    }        public void publish(String message, Map params){        if(subscribers == null)            return;        ArrayList<MessageSubscriber> subscriberList = subscribers.get(message);        if(subscriberList == null || subscriberList.isEmpty())            return;        for (MessageSubscriber topicSubscriber : subscriberList)            topicSubscriber.onRecived(message,params);    }}

该类中主要包含两个方法:register用来注册消息订阅者,publish用来发送消息。这样,对某个事件感兴趣的模块就可以通过MessagePublisher来订阅这个相关的消息,而产生事件的模块就可以通过MessagePublisher将其发布出去,例如在新建用户逻辑的后面就可以加上以下代码片段
Map params = new LinkedHashMap();params.put("id",newUserId);params.put("name",newUserName);MessagePublisher.instance().publish("/UserManagment/User/Add",params);
  本文中的实例的唯一过滤依据就是消息的名称,在实际的项目中,过滤的方式可能更复杂,订阅者会提供其他过滤条件,只有这些条件被满足时,消息才会被发送给订阅者。另外,当消息发送出去后,订阅对象的onRecived方法会依次被调用,所以,订阅对象不应该在该方法中执行耗时操作,最好另外启动一个新线程来处理消息,不然会阻塞其他订阅对象收到该消息。
  消息订阅机制的优点是大大降低了模块间的耦合度,相关操作都集中在MessagePublisher中;另外一个优点就是可扩展性强,随着系统越来越复杂,可以考虑把消息订阅和分发机制单独作为一个模块来实现,增加新特性以满足需求。消息订阅机制的缺点是发送者不能获知订阅者的执行情况。