设计模式在实际项目中的应用
概要
Leader 设计并实现了一个基于OSGI的架构,不同的功能模块以插件的形式溶入这个系统。
由于使用了ibatis,所以不同模块注入后就产生了一个需求。需要提供一个管理器,管理ibatis的配置文件,实现动态的加载卸载不同模块。这个任务交给了我。
需求分析
Hibernate支持配置文件的融合。但是ibatis我没有找到相应的解决方案。
这个管理器需要实现配置文件的融合、分离、动态的生成SqlMapClient以及DaoManager还要在相应的操作结束后打印日志,甚至需要给使用这个管理器的用户提供很高的灵活性和可扩展性。用户可能只注册(注销)模块,但是不马上更新SqlMapClient以及DaoManager,所以需要管理器缓存某些操作以及一些信息。
设计与实现
当一个新的模块注入以后,需要通知管理器,管理器会根据条件判断是否去重新加载新的配置文件并替换目前的SqlMapClient以及DaoManager的实例。注册后,注销前,重新生成实例这3个阶段,需要处理不同的事件。
这个过程可以效仿swing中,在按钮上的事件监听。为管理器添加监听器,不同的监听器完成不同的功能。所完成的功能由使用者定制。管理器只是去执行特定的监听器上的方法。这样可以给客户提供很大的灵活性。
观察者模式(the Observer pattern)进入我们的视野。
这个模式的一个关键是:管理器不需要知道是谁触发了事件,如何去处理这些事件,它的职责就是完成xml的注册、注销、更新2个对象。管理器会在不同方法被调用时,迭代已经注册的tracker,触发tracker的不同事件。如果想给管理器添加不同的事件(如增加日志输出、校验、whatever),用户只需要在触发管理器不同的事件后,为管理器添加不同的功能的tracker即可。而不需要去更改被观察的对象(管理器)。增加跟踪器无需更改被观察者。
这个过程很像在面板上添加多个按钮,每个按钮完成什么操作面板并不知道,当触发了按钮,按钮会完成一个由客户定义的操作。不同的操作,不会引起面板的逻辑的改变。变的只不过是不同的adapter。
使用了the Observer pattern 难道就可以解决所有的问题吗?
答案是否定的。
问题有2个,用户在调用管理器的方法获得SqlMapClient,DaoManager的时候:
1 如何去判断这个SqlMapClient,DaoManager是否需要更新;
2 如何让SqlMapClient,DaoManager针对更新作出调整呢?
我们可以用另一个模式去解决以上的问题: 代理(the Proxy Pattern)
对SqlMapClient,DaoManager做代理;事件驱动SqlMapClient,DaoManager的重置。
代理的作用是:
将系统中最新的SqlMapClient,DaoManager对象的实例,替换系统中的已有的实例。并且在更新实现相应的事件或者提供不同的事件。
管理器可以通过透明代理的方式,让客户感觉不到SqlMapClient,DaoManager实例的变动。
具体的接口设计:
public interface ISqlMapBuilderManager {
……
/**
*注册sql map 配置信息 注册成功后该方法会触发Tracker的afterSqlMapConfigRegistered
* 如果注册引发client或daomanager重建(由isAutoRebuild()决定),
* 则会触发Tracker的afterSqlMapClientRebuild/afterDaoManagerRebuild
*
* @param id
* 配置id
* @param inputStream
* 配置信息输入流
*/
public void registerSqlMapConfig(Object id, InputStream inputStream)
throws IbatisBuildException;
/**
* 注销sql map配置信息 注销前该方法会触发Tracker的beforeSqlMapConfigUnregistered
* 如果注销引发client或daomanager重建(由isAutoRebuild()决定),
* 则会触发Tracker的afterSqlMapClientRebuild/afterDaoManagerRebuild
*
* @param id
*/
public void unregisterSqlMapConfig(Object id) throws IbatisBuildException;
/**
* 获得sql map client 并指定是否重新创建
*
* @param rebuild
* 是否重新创建
* @return
*/
public SqlMapClient getSqlMapClient(boolean rebuild);
public void addTracker(ISqlMapTracker tracker);
……
}
public interface ISqlMapTracker {
/**
* 获得 tracker 的 id,该id用于注册
* @return
*/
public String getId();
/**
* 当新的配置信息被注册事触发
* @param id 配置id
* @param configuration 配置信息
*/
public void afterSqlMapConfigRegistered(Object id,IConfiguration configuration);
/**
* 当配置信息注销前触发
* @param id
* @param configuration
*/
public void beforeSqlMapConfigUnregistered(Object id,IConfiguration configuration);
/**
* 当SqlMapClient对象被重新构建后触发
* @param sqlMapClient
*/
public void afterSqlMapClientRebuild(SqlMapClient sqlMapClient);
/**
* 当DaoManager重新构建后触发
* @param daoManager
*/
public void afterDaoManagerRebuild(DaoManager daoManager);
}
透明代理的实现:
public class SqlMapBuilderManager implements ISqlMapBuilderManager {
……
public SqlMapClient getSqlMapClient(boolean rebuild) {
if (rebuild) {
SqlMapClientProxy proxy = new SqlMapClientProxy(this.sqlMapClient);
this.addTracker(proxy);
return proxy;
} else {
return sqlMapClient;
}
}
……
}
public class SqlMapClientProxy implements SqlMapClient, ISqlMapTracker {
……
public SqlMapClientProxy(SqlMapClient sqlMapClient) {
this.sqlMapClient = sqlMapClient;
}
public void beforeSqlMapConfigUnregistered(Object id,
IConfiguration configuration) {
System.out.println("sqlManager 已经卸载成功");
}
……
}
测试代码:
public void test() {
SqlMapBuilderManager builderManager = new SqlMapBuilderManager();
try {
builderManager.registerSqlMapConfig("1",
new FileInputStream("sql-map-config3.xml"));
assertNull(builderManager.getSqlMapClient());
SqlMapTrackerAdapters trackerAdapter = new SqlMapTrackerAdapters();
builderManager.addTracker(trackerAdapter);
builderManager.setAutoRebuild(true);
builderManager.registerSqlMapConfig("2",
new FileInputStream("sql-map-config4.xml"));
assertNotNull(builderManager.getSqlMapClient(true));
builderManager.registerSqlMapConfig("3",
new FileInputStream("sql-map-config5.xml"));
builderManager.unregisterSqlMapConfig("2");
assertNotNull(builderManager.getSqlMapClient(true));
} catch (Exception e) {
e.printStackTrace();
}
}
配合上面提到的实现以及这个测试,我们可以了解builderManager的工作过程:
我们可以为管理器设置不同的监听器,以增加不同的功能。但是扩展对于管理器本身来说是透明的。代理让SqlMapClient的更新变得透明,而且提供了Tracker特定事件的实现。
实现的过程中,很容易写出来如下的代码:
private void runTrackerAfter(Object id, IConfiguration result) {
for (Iterator it = trackerList.keySet().iterator(); it.hasNext();) {
String tId = (String) it.next();
ISqlMapTracker tracker = (ISqlMapTracker) this.trackerList.get(tId);
tracker.afterSqlMapConfigRegistered(id, result);
}
}
private void runTrackerBefore(Object id, IConfiguration unUse) {
for (Iterator it = trackerList.keySet().iterator(); it.hasNext();) {
ISqlMapTracker tracker = (ISqlMapTracker) it.next();
tracker.beforeSqlMapConfigUnregistered(id, unUse);
}
}
private void runTrackerAfterBuild() {
for (Iterator it = trackerList.entrySet().iterator(); it.hasNext();) {
ISqlMapTracker tracker = (ISqlMapTracker) it.next();
tracker.afterSqlMapClientRebuild(sqlMapClient);
}
}
迭代模式和回掉模式的应用:
多么重复丑陋的小方法呀! ^_^ 我们仍然可以使用模式去让代码变得强壮起来:
private void trackersWalker(ITrackerProcessor processor) {
for (Iterator it = trackerList.values().iterator(); it.hasNext();) {
ISqlMapTracker tracker = (ISqlMapTracker) it.next();
processor.process(tracker);
}
}
interface ITrackerProcessor{
public void process(ISqlMapTracker tracker);
}
private void runTrackerBefore(final Object id, final IConfiguration unUse) {
this.trackersWalker(new ITrackerProcessor(){
public void process(ISqlMapTracker tracker) {
tracker.beforeSqlMapConfigUnregistered(id, unUse);
}});
}
结束语
到这里,需求虽然已经实现,但是对代码的优化重构还远远没有结束。在项目中学到很多宝贵的经验,非常感谢leader-石鑫的指导。非常感谢!