耦合与内聚的应用 —— 事件驱动模式与解耦
2014-07-21 22:03
716 查看
关于解耦
解耦的概念很早就有,也知道什么叫解耦,但一直没有进行一些系统的学习,缺乏思考,很难准确的说出为何解耦、如何解耦以及解耦的应用场景。前两天在看《架构之美》这本书时突然看到了关于解耦与内聚的描述,想要系统的了解一下却发现网上相关的总结与资料甚少,于是激发了我想要写一些关于解耦应用、提高内聚相关的知识总结。
本人知识面有限所以就简单的定了一个《耦合与内聚的应用》这样的题目,如果内容有不妥的地方望各位看官指点。
关于事件驱动模式
事件驱动模式其实接触的很早,最开始学习Java Swing时就已经接触到事件的概念,之后在做ExtJS开发的时候事件驱动也被大量运用于框架以及自定义组件中。本文主要内容为解耦,所以将讲述该模式的解耦使用场景,具体的实现也将不在此累述。目前有 Google 的 Guava 框架有现成的事件模型代码,本文也将以此为基础作讲解。如有不知道的同学可以自行找度娘问一下。
解耦场景
谈到耦合性必然聊及内聚性,如果需要脑补的同学也可先前往脑补,一般较低耦合的情况下必然各组件的内聚性较高。下面思考这样一个场景:我们需要实现一款下载软件,不光需要实现下载,同时需要对下载过程进行实时反馈,提供给使用者查看,不光要在主要界面上查看,还需要在桌面小图标上显示(这个场景也很常见,如果不理解可以参照迅雷、Flashget等下载软件的功能)。
从传统的多层架构来说,我们并不希望下载功能的代码与显示内容的代码有任何关联,一旦出现两个功能的代码出现关联,显示内容的任何修改都将影响下载功能的代码,这将导致软件可维护性变得非常低。下面我来看一下这个下载功能的最基础的实现代码。
public class DownloadComponent { public void download(String url) throws Exception { // 这里下载的代码。 InputStream is = new URL(url).openConnection().getInputStream(); int total = is.available(), data = -1, current = 1; while ((data = is.read()) != -1) { // 这里是界面用的代码 System.out.println("current / total = " + (current++) + "/" + total); } }
解耦方法
上面只是一些简单的实例代码,用于模拟一个下载的场景,真实的下载器会更加复杂。如果我们需要将打印语句处的变化为显示百分比,参数定义都会需要修改。两个功能的内聚性非常高,修改起来非常困难。那么我需要对这个代码进行一些改造。我们默认采用Guava工具包作为事件总线的实现,当然你也可以使用JavaSDK自带的EventListenerList自行实现一套事件驱动功能。我们首先将两个功能独立开,创建两个组件,一个叫DownloadView,一个叫DownloadComponent。并分别提供两个方法,一个用于显示,一个用于下载。
public class DownloadView { public void display(int current, int total, int data) { System.out.println("current / total = " + (current++) + "/" + total); } }
DownloadView:将决定如何显示current、total、data的内容。
public class DownloadComponent { private EventBus bus; public DownloadComponent(EventBus bus) { this.bus = bus; } public void download(String url) throws Exception { InputStream is = new URL(url).openConnection().getInputStream(); int total = is.available(), data = -1, current = 1; while ((data = is.read()) != -1) { bus.post(new DownloadEvent(current++, total, data)); } } }
DownloadComponent:将决定如何下载,并通过EventBus进行解耦,该DownloadComponent将不实现如何展示data、current、total参数,并通过DownloadEvent将参数传递出去,具体由谁在处理、如何处理,DownloadComponent是不清楚的。这样提高了DownloadComponent的内聚性,明确了该组件功能的单一性。
注意DownloadEvent事件类型,该对象将作为一个消息对象在两个组件中传递。但消息对象其实并没有完全解除DownloadComponent与DownloadView的耦合(也有朋友通过Map进行消息传递,其实该方法也无法完全解除耦合),实质上DownloadView还是会对DownloadComponent有一定程度上的耦合,但是已经降至非常低可以忽略。
public class DownloadEvent { private int current; private int total; private int data; public DownloadEvent(int current, int total, int data) { this.current = current; this.total = total; this.data = data; } public int getCurrent() { return current; } public int getTotal() { return total; } public int getData() { return data; } }
完成了消息对象,我们需要定义一个处理消息对象的处理器,用来连接DownloadView与DownloadEvent之间的关系。
public class DownloadViewSubscriber { private DownloadView view; public DownloadViewSubscriber(DownloadView view) { this.view = view; } @Subscribe public void displayView(DownloadEvent event) { view.display(event.getCurrent(), event.getTotal(), event.getData()); } }
最后提供main调用方法。
public static void main(String[] args) throws Exception { // 创建代码一般交给 IOC 管理。 EventBus bus = new EventBus(); DownloadComponent component = new DownloadComponent(bus);// 下载组件。 DownloadView view = new DownloadView();// 显示组件 DownloadViewSubscriber subscriber = new DownloadViewSubscriber(view); bus.register(subscriber);// 注册监听者。 component.download("http://baidu.com"); }
平时我们更多的查看设计模式时,并没有太多的关注具体的应用场景,本文提供了一种解耦的方式。
相关文章推荐
- 小议解耦合--应用设计模式
- 浅谈JSF的两大两点:面向组件编程和事件驱动模式
- Adapter模式在J2SE事件处理中的应用
- Workflow Foundation 4.0中的事件驱动流程设计和应用(二)
- Workflow Foundation 4.0中的事件驱动流程设计和应用(一)
- (转载)总结应用和驱动之间用事件通讯的办法
- Workflow Foundation 4.0中的事件驱动流程设计和应用(五)
- 总结应用和驱动之间用事件通讯的办法
- MSP430的低功耗事件驱动工作模式
- Workflow Foundation 4.0中的事件驱动流程设计和应用(四)
- Adapter模式在J2SE事件处理中的应用
- 如何在驱动层和应用层之间共享事件?
- Adapter模式在J2SE事件处理中的应用
- 一个应用策略模式(Strategy)的小实例----对TreeView功能菜单的功能选择模块进行解耦重构
- Adapter模式在J2SE事件处理中的应用
- 小议解耦合--应用设计模式
- 事件驱动的架构及应用
- 总结应用和驱动之间用事件通讯的办法(转)
- Workflow Foundation 4.0中的事件驱动流程设计和应用(二)
- Workflow Foundation 4.0中的事件驱动流程设计和应用(四)