为何愈来愈多的企业应用开发正在转向组件框架和解决方案?组件架构是否有前途?我相信答案是确定的,并且很快全部开发框架都将会是基于组件的——这是近在眼前的事情。下面让我来向你揭示这一切的缘由。html
你怎么来建设你的房子?通常你会从砌块开始。咱们能够将构建Web应用与构建你的乡间小屋进行对比。你可以快速构建一个很是好看的应用,并且它具备全部必需的功能。一样,在你的房子里面,每一间房间都是针对具体的需求来建立的,例如厨房、起居室、卧室或浴室。房子的布局使你可以经过走廊和楼梯很方便地在房间之间移动。java
如今你可以作得更好,并且可以承担建设一座更大更好的房子的投入——你也许但愿拥有桑拿房、游泳池、影院以及一座尽是爬行动物的巨大的水族馆☺。但要想改变房子的设计倒是件很是困难的事情。若要添加额外的设施,房子最终看起来也许就不那么漂亮了。此外,因为你添加的这些设施必须放在不太方便的位置,它们也会影响房子使用的便利性,例如你必须穿过主卧室才能进入台球室。web
最后,你那漂亮又整洁的房子将拥有一堆不一样的功能,但它会变得笨拙又不温馨。一样的道理也适用于应用开发。架构
问题是,有没有可能设计一款应用,可以根据你的需求成长和改变?oracle
组件是应用的积木式构件app
组件是扩展应用功能的首要方法。建立组件的过程,与基于组件建立应用的过程<a name="_GoBack">有一些差别。组件不止应该提供有用的功能,还应该从一开始就设计成可复用的。框架
组件复用布局
组件应该采用松耦合方式设计以便于复用。为实现这一目标,不一样的框架每每基于观察者模式实现其事件模型。该模式容许多个接收者订阅同一事件。post
观察者模式的实现最先出如今Smalltalk中。Smalltalk是一个基于MVC的用户界面框架,如今它已经成为MVC框架的关键部分。我但愿你能注意到,自Java 1.0版本起,观察者模式就已经在Java中存在。下面让咱们深刻了解它。动画
下面的UML图展示了观察者模式:
如下则是一段基本的Java实现:
public class ObservableX extends Observable { ... public void setAmount(double amount) { this.amount = amount; super.setChanged(); super.notifyObservers(); } } public class ObserverA implements Observer { public void public void update(Observable o) { // gets updated amount } } public class ObserverB implements Observer { public void public void update(Observable o) { // gets updated amount } } //instantiate concrete observableX ObservableX observableX = new ObservableX(); //somewhere in code observableX.addObserver(new ObserverA()); observableX.addObserver(new ObserverB()); //much later observableX.setAmount(amount);
它是这样工做的:
首先咱们建立一个ObservableX类的实例,将ObserverA和ObserverB实例添加到observableX对象中,而后在代码中的某个位置,咱们使用setAmount方法设定“必定数量”的值。被观察者(observable)类的功能将接收到的数量通知全部注册的观察者。
观察者担当着中介的角色,维持接收者的列表。当组件里有事件发生时,该事件将被发送到列表上的全部接收者。
因为这个中介角色的存在,组件并不知道其接收者。而接收者能够订阅某个特定类型的不一样组件中的事件。
当一个类使用事件将自身的变化通知观察者时,该类就能够成为一个组件。而这能够经过观察者模式来实现。
使用组件比建立组件容易
经过使用组件,你可以快速建立各类窗体、面板、窗口以及界面中的其余合成元素。不过,为了可以复用新的由组件建立的合成部分,应该将它们转化为组件。
为了实现这一目标,你须要决定组件所要生成的外部事件,以及消息传递机制。例如,你至少须要建立新的事件类而且定义接口或回调方法以接收这些事件。
这个方式让实现可复用的应用组件变得更复杂。当系统只是由少许合成元素组成时没什么问题——这时合成元素最多不超过10个。然而当系统包含数以百计的此类元素时,又当如何?
与之相反,不听从这一方式将致使元素间的紧耦合,而且会把复用的机会下降到0。这反过来会致使代码复制,从而让将来的代码维护变得更复杂,并将致使系统中的bug数量上升。
因为组件使用者每每不了解如何定义和传递他们本身的新事件,问题将变得更为严重。但他们能够轻松地使用组件框架提供的现成的事件。他们知道如何接收但不知道如何发送事件。
为了解决这个问题,让咱们考虑如何简化应用中使用的事件模型。
太多的事件监听者
在Java Swing、GWT、JSF和Vaadin中,观察者模式被用于实现多用户可以订阅同一事件的模型,并将用于添加事件监听者的列表做为其实现方式。相关事件一旦发生,将被发送到列表上的所有接收者。
每一个组件为一个或多个事件建立本身的事件监听者集合。这将致使应用中类的数量不断增多。反过来,这也会使系统的支持和开发变得更复杂。
借助注解机制(annotation),Java找到了一条让单个方法订阅特定事件的道路。例如,考虑Java EE 6里的CDI(Contexts and Dependency Injection,上下文和依赖注入)中对事件模型的实现。
public class PaymentHandler { public void creditPayment(@Observes @Credit PaymentEvent event) { ... } } public class PaymentBean { @Inject @Credit Event<<paymentevent> creditEvent; public String pay() { PaymentEvent creditPayload = new PaymentEvent(); // populate payload ... creditEvent.fire(creditPayload); } }
你能够看到,当PaymentBean对象的pay()方法被调用时,PaymentEvent被触发。接下来PaymentHandler对象的creditPayment()方法接收了它。
另外一个例子是Guava类库中事件总线的实现:
// Class is typically registered by the container. class EventBusChangeRecorder { @Subscribe public void recordCustomerChange(ChangeEvent e) { recordChange(e.getChange()); } } // somewhere during initialization eventBus.register(new EventBusChangeRecorder()); // much later public void changeCustomer() { ChangeEvent event = getChangeEvent(); eventBus.post(event); }
EventBus注册了EventBusChangeRecorder类的对象。接下来对changeCustomer()方法的调用会使EventBus接收ChangeEvent对象并调用EventBusChangeRecorde对象的recordCustomerChange ()方法。
如今你不须要为你的组件实现若干事件监听者,在应用中使用事件也变得更简单了。
当全部组件都同时在屏幕上展示时,使用事件总线是很方便的。以下图所示,它们使用事件总线进行消息交换。
这里,全部元素——标题、左侧的菜单、内容、右侧的面板——都是组件。
订阅事件——别忘记取消订阅
经过将事件监听者替换为注解,咱们在简化事件模型使用的道路上前进了一大步。但即便如此,系统中的每一个组件依旧须要链接到事件总线,而后,必须订阅上面的事件并在正确的时间取消订阅。
当相同的接收者屡次订阅同一个事件时,将会出现许多重复提醒,这种状况很容易出现。而类似的状况还会在多个系统组件订阅同一事件时发生,这将会触发一系列级联事件。
为了能更好地控制事件模型,将工做与事件一块儿迁移到配置中,并让应用容器负责事件管理是颇有意义的。因为特定的事件仅在特定条件下有效,将这些事件的状态管理迁移到配置中也是合理的。
下面是一段配置的例子:
<?xml version="1.0"?> <application initial="A"> <view id="A"> <on event="next" to="B"/> </view> <view id="B"> <on event="previous" to="A"/> <on event="next" to="C"/> </view> <view id="C"> <on event="previous" to="B"/> <on event="next" to="D"/> </view> <view id="D"> <on event="previous" to="C"/> <on event="finish" to="finish"/> </view> <final id="finish" /> </application>
视图A中的“下一个(next)”事件触发了向视图B的转变。在视图B中,用户能够经过“前一个(previous)”事件回到A,或是经过“下一个(next)”事件进入C。D视图中的结束事件将转入“最终(final)”状态,将通知应用结束其中的工做流。
有限状态机是专为这样的需求设计的。状态机是一种数学计算模型。它被设想为一种抽象的机器,能够处于有限数量的状态中的一个,而且在同一时间里只会处于一个状态——这被称为当前状态。事件或条件将触发向另外一个状态的转变。使用这一方式,你可以轻松地定义活动画面,并让某事件来触发向另外一个画面的转变。
使用有限状态机来配置应用的好处
大部分状况下,应用配置是静态定义的。使用依赖注入配置应用,咱们在启动时定义应用结构。但咱们忘记了在探索应用的过程当中它的状态可能会改变。在应用的代码中,状态改变每每属于硬编码,它让将来的调整和维护变得复杂。
将状态间的转变迁移到配置中能够提升灵活性。并且这正是为何咱们在建立诸如窗体、窗口或面板等复杂应用元素时,无需为了应用应该进入哪一个状态而操心。你能够稍后来处理它们,在配置中设定其行为。
全部组件均可以使用标准的事件发送机制进行交流——即经过事件总线。同时,状态机可以控制组件事件到事件总线的订阅。这一方式将应用的所有组件(窗体、窗口、面板)变为可复用组件,能够经过外部配置轻松地管理它们。
若是有兴趣,你能够看一下Enterprise Sampler中一些配置的例子。
你也能够将状态配置看做城市的公路图,把事件看做递送商品的汽车,而将城市里的人看做目的地。
我确信采用这样的方式,不只可以轻松地设计和构建一间规模虽小却作好了成长准备的房子,还可以建设拥有摩天大楼、公路和高速公路的城市。
关于做者
Aliaksei Papou是Lexaden.com的CTO、软件架构师和联合创始人,他拥有超过10年的企业级应用开发经验,他对于技术创新有着强烈爱好。他与Lexaden.com的CEODenis Skarbichev(另外一位联合创始人)一同开发了能够建立大规模敏捷企业级应用的 Lexaden Web Flow语言。