存档

文章标签 ‘Design Pattern’

Daniel-Journey Weekly Dose-2010/5/29

2010年5月30日 admin 1 条评论

Design Pattern

Asynchronous Completion Token

Architecture

演化架构与紧急设计: 研究架构和设计

“更好的定义是:‘在大多数成功的软件项目中,从事该项目的专家开发人员对设计系统的设计存在共识。这种共识被称为 “架构”。这种共识包括如何将系统分为组件以及组件如何通过接口进行交互。’”

有三个问题可能会产生偶发复杂度。我已经讨论了第一个:由于日程或其他外部压力而导致临时大量削减代码。第二个是复制,The Pragmatic Programmers 中称其为对 DRY(不要重复自己,Don’t Repeat Yourself)原则的违背。复制是软件开发中最能让人在不知不觉中降低软件质量的因素,因为在开发人员甚至还没有觉察的情况下,它会蔓延到许多位置。一个明显的例子是复制并粘贴代码,但是也存在大量更复杂的示例。

偶发复杂度的第三个诱因是不可逆性。您做出的无法逆转的所有决定都将最终导致某种程度的偶发复杂度。不可逆性将同时影响架构和设计,尽管它的影响在架构级别上比较常见并且比较有破坏性。尽量避免作出不可逆转或者难于逆转的决定。我的一些同事信奉在最后责任一刻 做决定。这并不表示您应当把决定推迟得太久,只要推迟得比较久就足够了。什么时候才是决定某些架构关注点的最后责任时刻?做决定的时间拖得越晚,您为自己留下的可能性就越多。问问您自己:“我是不是现在就需要做那个决定?” 以及 “我做什么能让我推迟那个决定?” 如果在决策过程中应用一些技巧,那么您将会对您可以推迟的内容感到十分惊讶

 

演化架构与紧急设计: 积累惯用模式

Big Design Up Front 设计方法学的支持者在开始编写代码前,要花费大量的时间确定当前应用程序的所有必需的设计元素。编制的文档中的大多数内容对于解决方案的总体设计仍是重要的。但是,在实现软件的过程中,会不断遇见意外。实现的每个设计元素与其他设计元素相互联系,形成极端复杂的依赖和关系网络。代码中有些方面本以为平常不过,但是一旦要实现系统中其他所有必需的部分时,复杂度又随之放大。由于不能理解代码中不同设计元素之间复杂的相互作用,导致在估算完成解决方案所需的努力时困难重重。在软件方面,估算仍是一种玄妙的 “黑色艺术”,因为对于如此复杂的耦合和交互网络,实在是难以理解,因而也难以分析。

依赖于紧急设计的敏捷方法学则尝试一种不同的方法。敏捷架构和设计在编写代码前也不会避开设计,但是它们的实际工作者已经知道,只有等到实现了重要的部分后,才能彻底地理解整个问题。紧急设计中的开发技巧使您可以推迟做决定,直到掌握了更多的上下文。精益软件运动(见 参考资料)有一个很好的概念叫做 最后可靠时刻(last responsible moment):不是将决定推迟到最后时刻,而是最后可靠时刻。越是往后推迟设计决定,就能掌握越多的信息,从而可以做出更精妙、更符合实际的决定。

 

Webwork in action

Webwork是一个优越的框架,它的设计基于这样一个基本原理:完成通用任务应该是简单的,而构建高级的设计也应该是可行的。

Webwork是一个MVC框架,但同时它也是一个轻量级容器。

在Rickard Oberg(Webwork的创作者和JBoss创始人之一)构建最初版本Webwork的时候,他曾说过“框架的强大之处不是源于它能让你干什么,而是它不能让你做什么”。Rickard说过的话解释了什么是框架:框架使混乱的东西变得结构化。

我们再来一个比方。框架就像是一间有很多房梁的房子,当你需要扩建房子的时候,譬如增加新的房间、窗户、或者卧室增加一个壁炉,由于房梁的限制,你并没有其他的选择。虽然,较少的屋梁和架构会让你有更多的选择,但是当台风来袭或者发生地震的时候,你让你的家人住在一间只有屋顶的房子里,恐怕也不会觉得安全吧。

由于框架会要求更多的结构,你的创造力所拥有的发挥空间开始收缩了。一种极端的情况就是框架消失了,一片混乱;而另一种极端就是框架留给你的选择是如此之少,以至于你无法完成你的应用程序。很明显,一种完美的中间状态存在于这个极端之间的某一处,而这种中间状态就是Webwork和所有其他MVC框架所梦寐以求的。

概念性架构设计

很多人认为从需求分析到架构设计之间的过渡遇到很多问题,究其根源,可能是以下的原因造成的:

  1. 用例是面向问题领域的,而设计是面向机器域的,这两个‘空间’存在映射。
  2. 用例技术本身不是面向对象的,而设计应该是面向对象的,这是两种不同的思维方式。
  3. 用例规约采取自然语言描述,而设计采取形式化的模型描述,描述的方式不一样。

Daniel-Journey Weekly Dose –2010/5/22

2010年5月22日 admin 没有评论

Agile

敏捷软件开发模型:SCRUM
闲话:关于敏捷

而敏捷里面所有的实践,质量是隐含的,不可侵犯的.这就是我们所说的Built Quality In.比如,刚开始写Story, 就要和测试人员定义验收条件;开发Story采用TDD的方式,通过测试来检验功能,类似于砌墙的时候先扯水平线,然后开始铺砖.

整个团队只有一个目标:产生高质量的交付

在此目标之下,以尊重 交流 反馈 勇气 简单为基本做事价值观和原则.

Agile Versus Waterfall: Part One

敏捷与能力

丰田精益生产方式中一个经常用的隐喻是“湖水和岩石”。大意是指湖水太深,你无法发现阻碍当前生产的主要原因,只有把湖水讲下去,才能发现真正的岩石在哪里。在精益生产中,湖水是指“库存”,而在软件开发中,对应的湖水则是“迭代周期”。
我们举一个例子,当发现“项目严重延期”时,通常已经是交付时间,不过开发人员最近一直加班,也挺辛苦的呀。不过如果你是项目经理或者客户,你知道开发人员的时间都花到哪儿了吗?如果采用迭代式交付,每两周一个迭代,完成一定的特性,你可能第一个迭代就发现问题

我们可以抱怨团队开发人员能力不够,不过这关敏捷的什么事儿?本来大家都知道的事情,只不过敏捷让它暴露的更严重更突出罢了,谁还会任由你“掩耳盗铃”呢。

敏捷和SCRUM回顾

Javascript

JavaScript Debugging Techniques in IE 6

Java

Top 10 (not so popular) Eclipse Shortcuts

Design Pattern

Design Patterns Uncovered: The Flyweight Pattern

image

When considering this pattern, you will need to think about intrinsic and extrinsic data. Intrinsic data is the data that makes this object instance unique. Meanwhile, extrinsic data is information that can be passed in through arguments. So, if you can make some data extrinsic for cases that you have a large number of objects, the Flyweight pattern may be exactly what you are looking for.

设计模式学习笔记(十二)——Flyweight享元模式

Flyweight模式的几个要点:

1、面向对象很好的解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。

2、Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象的状态处理。

3、对象的数量太大从而导致对象内存开销加大(这个数量要经过评估,而不能凭空臆断)

SOA

自底向上的ESB管理 VS. 自顶向下的SOA治理

但创建了一个SOA,自底向上的治理方法关注围绕单独ESB的集成化服务,这些ESB 可以快速装配。该方法因为需要过多的更新和重做而受到非难。

同时,相对的“自顶向下”的治理方法涉及大量严格规划和精确策略执行。这种方法也由于花费过多的时间才能产生结果而不被认可。

SOA治理策略如何抉择?

企业接受SOA的速度比较慢,大多数接受者倾向于接受自底向上的方法。

随着人们开始实施SOA,自身的技术就不够用了,这时就需要一些治理来辅助

Hariharan最后讲到:“如果企业以一种长期的愿景和蓝图来寻求SOA,那么自顶向下的方法是最佳的选择。如果并没有一项企业级的IT 策略以及长期的愿景,IT 可以选择自底向上的方法来实现和展示SOA的好处。”

Linux

使用scp 命令在两台linux上对拷文件或者文件夹

scp 本地用户名@IP地址:文件名1 远程用户名@IP地址:文件名2

拷贝文件夹命令如下:scp -r file username@ip:filepath
多加上一个-r参数即可。

Daniel-Journey Weekly Dose-2010/4/11

2010年4月11日 admin 3 条评论

Architecture & Design

Mediator Pattern applied to Javascript

The diagram below is a general representation of the interaction between a mediator and colleagues. Each colleague stores a reference to the mediator and the mediator stores a reference to each colleague. Colleagues are unaware of each other and messages are broadcast through the mediator.

image

Two of the benefits of using the Mediator pattern are that that it decouples colleagues and simplifies object interaction. Instead of using the Observer pattern to explicitly set many-to-many listeners and events, Mediator allows you to broadcast events globally across colleagues. The biggest drawback of using Mediator pattern is that due to the centralized control, the mediator can become difficult to manage.

向对象开发与面向组件开发的区别
养成面向组件编程(COP)的习惯
面向组件还是面向对象

既然类和组件有着这么多类似的地方,那么传统的面向对象编程和面向组件编程有什么区别呢?简单的说,面向对象关注的是组合在一个二进制可执行文件中的各个类的关系,而面向组件的编程关注的是在彼此独立的基础上模块之间的交互性,这种交互性使得你并不需要熟悉它们内部的工作原理。

面向对象与面向组件小议

面向对象技术的基础是封装--接口与实现分离,面向对象的核心是多态--这是接口和实现分离的更高级升华,使得在运行时可以动态根据条件来选择隐藏在接口后面的实现,面向对象的表现形式是类和继承。面向对象的主要目标是使系统对象化,良好的对象化的结果,就是系统的各部分更加清晰化,耦合度大大降低。 

组件技术的主要目标是复用--粗粒度的复用,这不是类的复用,而是组件的复用,如一个dll、一个中间件,甚至一个框架。一个组件可以有一个类或多个类及其它元素(枚举、)组成,但是组件有个很明oo显的特征,就是它是一个独立的物理单元,经常以非源码的形式(如二进制,IL)存在。一个完整的组件中一般有一个主类,而其它的类和元素都是为了支持该主类的功能实现而存在的。

最后强调一点,组件的目标是粗粒度的复用,组件的核心是接口。

用Qi4j进行面向组合编程

Java

SortedMap JavaDoc

Database and SQL

Efficient Pagination Using MySQL

高效的MySQL分页

SOA

服务和耦合的真正意义

基于设计的组件:首先:SOA和基于设计的组件不一样。在一个SOA架构中的服务是分布式的。而基于设计的组件中的这些组件是代码片段,通过他们所处每个系统来打包这些片段。从这种意义上来讲,EJB 能够作为服务和组件。

基于重用的组件通常比基于重用的服务简单。在服务重用时,在重用组件中变更带来的影响更是难以控制。其中可能有使用我的服务以及我没有关注的系统。在组件中,也许也是这种情况,但每个系统不得不有意识的做决定来升级组件,所以在一个系统中任何意外的破坏都与系统中那个早期的变更有关系。

耦合:这是“耦合”的真正意义。如果一个系统的改变很可能需要另一个系统的改变,那这两个系统是紧耦合的。这是一个很难通过观察代码来衡量的标准,然而很多人相信可以用代码的度量来理解耦合。

其他

一个老程序员的感悟:做技术二十多年,突然明白的道理

Oberserver模式的变种AsyncToken

2010年3月27日 admin 2 条评论

在周末的设计过程中,需要用到Observer模式,但当时纠结了一下是否真的需要使用Observer模式,在Google了一下以后,找到了[设计模式]AsyncToken模式,替换通常的Listener模式这篇文章。先贴一下样例代码吧,完整的代码可以参考这个连接

package cn.org.rapid_framework.util.concurrent.async; 

 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong; 

 import javax.swing.SwingUtilities; 

 /**
  * <pre>
  *      public void testSendEmail() {
  *          final String address = "badqiu(a)gmail.com";
  *          final String subject = "test";
  *          final String content = "async token test";
  *          AsyncToken token = sendAsyncEmail(address,subject,content);
  *
  *          token.addResponder(new IResponder() {
  *              public void onFault(Exception fault) {
  *                  System.out.println("email send fail,cause:"+fault);
  *                  sendAsyncEmail(address,subject,content);
  *              }
  *              public void onResult(Object result) {
  *                  System.out.println("email send success,result:"+result);
  *              }
  *          });
  *      }
  *
  *      public AsyncToken sendAsyncEmail(String address,String subject,String content) {
  *          final AsyncToken token = new AsyncToken();
  *
  *          Thread thread = new Thread(new Runnable() {
  *              public void run() {
  *                  try {
  *                      //do send email job...
  *                      token.setComplete(executeResult);
  *                  }catch(Exception e) {
  *                      token.setFault(e);
  *                  }
  *              }
  *          });
  *          thread.start();
  *
  *          return token;
  *      }
  * </pre>
  * @see AsyncTokenTemplate
  * @author badqiu
  */
 public class AsyncToken<T>  {
         public static final String DEFAULT_TOKEN_GROUP = "default";
         private static AtomicLong tokenIdSequence = new AtomicLong(1); 

         //tokenGroup tokenName tokenId   
         private String tokenGroup = DEFAULT_TOKEN_GROUP;
         private String tokenName;
         private long tokenId; 

         private List<IResponder> _responders = new ArrayList(2); 

         private UncaughtExceptionHandler uncaughtExceptionHandler;
         private T _result;
         private Exception _fault;
         private boolean _isFiredResult; 

         private CountDownLatch awaitResultSignal = null; 

         public AsyncToken(){
                 this(null);
         } 

         public AsyncToken(UncaughtExceptionHandler uncaughtExceptionHandler) {
                 this(DEFAULT_TOKEN_GROUP,null);
                 this.uncaughtExceptionHandler = uncaughtExceptionHandler;
         } 

         public AsyncToken(String tokenGroup,String tokenName) {
                 setTokenGroup(tokenGroup);
                 setTokenName(tokenName);
                 this.tokenId = tokenIdSequence.getAndIncrement();
         } 

         public String getTokenGroup() {
                 return tokenGroup;
         } 

         public void setTokenGroup(String tokenGroup) {
                 if(tokenGroup == null) throw new IllegalArgumentException("'tokenGroup' must be not null");
                 this.tokenGroup = tokenGroup;
         } 

         public String getTokenName() {
                 return tokenName;
         } 

         public void setTokenName(String tokenName) {
                 this.tokenName = tokenName;
         } 

         public long getTokenId() {
                 return tokenId;
         } 

         /**
          * addResponder(responder,false);
          * @param responder
          */
         public void addResponder(final IResponder<T> responder) {
                 addResponder(responder,false);
         }
         /**
          */
         public void addResponder(final IResponder<T> responder,boolean invokeResponderInOtherThread) {
                 _responders.add(responder); 

                 if(_isFiredResult) {
                         if(invokeResponderInOtherThread) {
                                 SwingUtilities.invokeLater(new Runnable() {
                                         public void run() {
                                                 fireResult2Responder(responder);
                                         }
                                 });
                         }else {
                                 fireResult2Responder(responder);
                         }
                 }
         } 

         public List<IResponder> getResponders() {
                 return _responders;
         } 

         public boolean hasResponder() {
                 return _responders != null && _responders.size() > 0;
         } 

         public UncaughtExceptionHandler getUncaughtExceptionHandler() {
                 return uncaughtExceptionHandler;
         } 

         public void setUncaughtExceptionHandler(UncaughtExceptionHandler uncaughtExceptionHandler) {
                 this.uncaughtExceptionHandler = uncaughtExceptionHandler;
         }        

         private void fireResult2Responder(IResponder responder) {
                 try {
                         if(_fault != null) {
                                 responder.onFault(_fault);
                         }else {
                                 responder.onResult(_result);
                         }
                 }catch(RuntimeException e) {
                         if(getUncaughtExceptionHandler() != null) {
                                 getUncaughtExceptionHandler().uncaughtException(responder, e);
                         }else {
                                 throw e;
                         }
                 }catch(Error e) {
                         if(getUncaughtExceptionHandler() != null) {
                                 getUncaughtExceptionHandler().uncaughtException(responder, e);
                         }else {
                                 throw e;
                         }
                 }
         } 

         private void fireResult2Responders() {
                 synchronized (this) {
                         _isFiredResult = true;
                         if(awaitResultSignal != null) {
                                 awaitResultSignal.countDown();
                         }
                 } 

                 for(IResponder r : _responders) {
                         fireResult2Responder(r);
                 }
         } 

         public void setComplete(){
                 setComplete(null);
         } 

         public void setComplete(T result) {
                 if(_isFiredResult) throw new IllegalStateException("token already fired");
                 this._result = result;
                 fireResult2Responders();
         } 

         public void setFault(Exception fault) {
                 if(fault == null) throw new NullPointerException();
                 if(_isFiredResult) throw new IllegalStateException("token already fired");
                 this._fault = fault;
                 fireResult2Responders();
         } 

         public boolean isDone() {
                 synchronized (this) {
                         return _isFiredResult;
                 }
         } 

         /**
          * @see Future
          */
         public Object waitForResult() throws InterruptedException,Exception {
                 return waitForResult(-1, null);
         }
         /**
          *  @see Future
          */
         public Object waitForResult(long timeout,TimeUnit timeUnit) throws InterruptedException,Exception {
                 synchronized(this) {
                         if(_isFiredResult) {
                                 if(_fault != null) {
                                         throw _fault;
                                 }else {
                                         return _result;
                                 }
                         } 

                         awaitResultSignal = new CountDownLatch(1);
                 } 

                 if(timeout > 0) {
                         awaitResultSignal.await(timeout,timeUnit);
                 }else {
                         awaitResultSignal.await();
                 } 

                 if(_fault != null) {
                         throw _fault;
                 }else {
                         return _result;
                 }
         } 

 } 

文章总结了AsyncTokenVS Observer好处和异同

使用AsyncToken的好处:

1. token可以无限传递,只要对方法的执行结果感兴趣,都可以监听方法的执行结果.

2. 拥有上下文,还可以引用前面的参数,以执行任务email重发这种任务

3. 一个token与一个方法对应,方法调用时你即知道token对应的事件,不需要使用listener模式中的一般用EventType来区别不现的事件

3. 灵活转换,也可以将上面的token再转至listener,再由listener以事件的方式派发事件

与Listener的异同:

1.token可以无限传递

2.没有使用事件或是监听不同的方法,listener一般配合需要使用事件,然后由事件进行参数的绑定.

3.listener模式一般是先设置好listener,而AsyncToken可以得到token后再添加监听方法

分类: 软件设计 标签:

Daniel-Journey Daily Dose-2010/2/12

2010年2月12日 admin 没有评论

Agile

Agile: The New Era

The study found that iterative and agile practices have a significant impact on defect and productivity factors, as indicated by the following points.

  • Releasing a system with 20% of the functionality complete is associated with a decrease in the defect rate of 10 defects per month per million lines of code as compared to waiting to release a product until 40% of the functionality is complete, and an increase in productivity of eight more lines of source code per person-day.
  • Continuous Integration, the idea of integrating and testing code as it is released to your source code repository, resulted in a decrease in the defect rate of 13 defects per month per million lines of code, and an increase in productivity of 17 lines of source code per person-day.

The first two are deeply embedded in the ideals of agile software development.

  • Releasing early and often to project stakeholders, using an iterative lifecycle.
  • Continuous integration, with daily builds including regression testing.
  • Teams with broad experience delivering multiple projects.
  • Careful attention to modular and loosely coupled, componentized architectures.

OOD and OOP

An (Overlooked?) Use Case for the Strategy Pattern

SOA

Enterprise Service Bus (ESB): a critical part of SOA

分类: 阅读 标签: , ,

Observer观察者模式以其应用

2009年10月3日 admin 没有评论

定义

定义对象间的一种一对多的关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知,并被自动更新。

使用的原因

在应用程序开发过程中,往往都要求用户界面和业务逻辑分离,划定清晰的界限.因为应用程序要求能快速的更改用户界面并且不能对应用程序其他部分产生连带影响,而且业务逻辑也会发生变化并要求这一切变化与用户界面无关.观察者(observer)就是解决此问题最常用的设计模式,它非常有助于在系统中各个对象之间划分清晰的界限。

还有一种常见的情况,一个软件系统常常要求在某一个对象发生变化的时候,某些其它的对象做出相应的改变,而观察者模式是解决这个问题的一个低耦合度的设计方案,它能够减少对象之间的耦合以便更加利于系统的复用。

未命名

用JDK中的Observable接口和Observer类实现Observer模式

由于Observer模式使用范围很广,所以在JDK中提供了Observable接口和Observer类方便开发人员使用Observer模式,而且使用也很简单。

  • 创建被观察者类,它继承自java.util.Observable类。
  • 创建观察者类,它实现java.util.Observer接口;
  • 对于被观察者类,添加它的观察者:

void addObserver(Observer o)

当被观察事件发生时,执行:

setChanged();

notifyObservers();

setChange()方法用来设置一个内部标志位注明数据发生了变化;notifyObservers()方法会去调用观察者对象列表中所有的Observer的update()方法,通知它们数据发生了变化。只有在setChange()被调用后,notifyObservers()才会去调用update()。

  • 对于观察者类,实现Observer接口的唯一方法update

void update(Observable o, Object arg)

形参Object arg,对应一个由notifyObservers(Object arg);传递来的参数,当执行的是notifyObservers();时,arg为null。

public class NumsObservable extends Observable {

public final static Integer ODD = 1;

public final static Integer EVEN = 2;

private int data = 0;

public int getData() {

  return data;

}

public void setData(int i) {

data = i;

Integer flag = EVEN;

if ((data & 0×0001) == 1)

  flag = ODD;

  setChanged();

notifyObservers(flag);

}

}

public class OddObserver implements Observer {

public void update(Observable o, Object arg) {

if (arg == NumsObservable.ODD) {

NumsObservable myObserable = (NumsObservable) o;

System.out.println("OddObserver:Data has changed to " + myObserable.getData());

}

}

S}

 

import java.util.Observable;

import java.util.Observer;

public class EvenObserver implements Observer {

public void update(Observable o, Object arg) {

if (arg == NumsObservable.EVEN) {

NumsObservable myObserable = (NumsObservable) o;

System.out.println("EvenObserver:Data has changed to " + myObserable.getData());

}

}

}

 

public class MultiTest {

public static void main(String[] args) {

NumsObservable number = new NumsObservable();

number.addObserver(new OddObserver());

number.addObserver(new EvenObserver());

number.setData(1);

number.setData(2);

number.setData(3);

}

}

在Spring框架中使用Observer模式

上面这个例程虽然很简单,但相信都大家了解了Observer模式的原理,而在实际的项目过称中,Observer模式的运用却复杂很多。例如,Observer对象本身还需要有很多依赖才能完成功能。下面这个例程针对的是如何在Spring框架下使用Observer模式,相信通过这段代码可以帮大家解决不少Observer模式使用的实际问题。

Subject接口代码

public interface Subject {
    void addListener(Observer observer);

    void removeListener(Observer observer);

    void notifyListeners();
}

Subject的具体实现

public class ConcretSubject implements Subject {
    private List<Observer> listeners=new ArrayList<Observer>();

    public void addListener(Observer observer) {
        listeners.add(observer);

    }

    public void notifyListeners() {
        for (Observer observer : listeners) {
            observer.update();
        }

    }

    public void removeListener(Observer observer) {
        listeners.remove(observer);

    }

}

Observer接口代码

public interface Observer {

    void update();

}

Observer接口的具体实现

public class ConcretObserver implements Observer{

    public void update() {
        System.out.println("ConcretObserver is updated");
    }

}

以上内容都是很普通的Java类,跟【用JDK中的Observable接口和Observer类实现Observer模式】的代码样例很类似,不同的是【用JDK中的Observable接口和Observer类实现Observer模式】的这个样例中Observer是以代码的形式注册到Observerable实现类中的,而在这个例程中借助Spring框架的MethodInvokingFactoryBean我们用配置的方式来实现以上的功能。

<beans>
    <bean id="subject" class="com.gaoke.simpleerp.service.impl.ConcretSubject" />
    <bean id="observer" class="com.gaoke.simpleerp.service.impl.ConcretObserver" />
    <bean id="registerConcretObserver"
        class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject">
            <ref local="subject" />
        </property>
        <property name="targetMethod">
            <value>addListener</value>
        </property>
        <property name="arguments">
            <list>
                <ref bean="observer" />
            </list>
        </property>
    </bean>
</beans> 

上面这个例程介绍的是如何通过Spring提供的MethodInvokingFactoryBean来实现Observer模式。由于Spring提供了强大的IOC容器功能,过Spring容器来实现Observer模式会让我们的应用程序开发的时候能更随心所欲:-)。

参考

Java:应用Observer接口实践Observer

Spring Loaded Observer Pattern