Preface:
合理的软件架构设计其好处是不言而喻的,系统具有清晰的软件结构,良好的可扩展性,类的职能单一明确,系统的复杂度底。此前的一个实际项目中总结了些关于OO设计的实际应用,主要是围绕‘高内聚及松耦合’,‘开闭原则’的一些应用。
Problem:
目前有一个实际应用放在我们面前,为一个银行现有BI系统开发WebService对外数据接口应用,数据交换方式以预定请求及响应报文来完成,要求可以数据接口系统跨平台使用。即远程客户端发来一种XML数据请求报文,系统按类型执行查询,然后返回XML数据响应报文。
问题也浮出水面,通常此类系统中我们可以想像到,其中一定会有一系列的if else来判断是何种请求报文,然后再执行对应的动作,但我们如果我们这样设计,系统就违反了开放-封闭原则 (OCP,Open-Close Principle),日后的扩展一定需要修改原有代码,而我们期望的是日后添加一种新报文后,只在系统中扩展新的请求、查询及响应对象来实现新需求。
带着问题思考解决办法...
补充:敏捷设计扩展知识手册
拙劣设计的症状:
1.僵化性(Rigidity):设计难以改变。很难对系统进行改动,因为每个改动都会迫使许多对系统其他部分的其他改动。
2.脆弱性(Fragility):设计易于遭到破坏。对系统的改动会导致系统中和改动的地方在概念上无关的许多地方发现问题。3.牢固性 (Immobility):设计难以重用。很难解开系统的纠结,使之成为一些可在其他系统中重用的组件。
4.粘滞性(Viscosity):难以做正确的事情。做正确的事情比做错误的事情要困难。
5.不必要的复杂性(Needless Complexity):过分设计。设计中包含有不具有任何直接好处的基础结构。
6.不必要的重复性(Needlsee Repetition):滥用复制/粘贴。设计中包含有重复的结构,而该重复的对象本可以使用单一的抽象进行统一。
7.晦涩性(Opacity):很难阅读、理解。没有很好的表现出意图。
面向对象的设计原则:
1.单一职责原则 (SRP,Single Resposibility Principle)
2.开放-封闭原则 (OCP,Open-Close Principle)
3.Liskov替换原则 (LSP,Liskov Principle)
4.依赖倒置原则 (DIP,Dependicy Independent Priciple)
5.接口隔离原则 (ISP,Interface Seperation Principle)
Solution:
我们初步的想法是,系统接受到一种XML请求后将其转换成请求对象,类似多态的方法,根据不同的请求对象由查询工厂来创建返回不同的查询处理类,再由查询处理类返回填充好的数据响应对象,最后转换成XML响应报文。由此思路,我们完成了UML类图设计,如下:
首先是RemoteQueryService 接口,系统对外的WS服务由此接口完成。
/**
* 类说明: WS远程服务接口<br>
* 创建时间: 2009-11-6 上午10:07:55<br>
*
* @author Seraph<br>
* @email seraph115@gmail.com<br>
*
*/
public interface RemoteQueryService {
/**
* 功能说明: <br>
* 创建者: Seraph<br>
* 创建时间: 2009-11-6 上午10:08:09<br>
*
* @param request
* @return
*/
@Profiled(tag = "RemoteQueryService")
public String doQuery(String request);
}
RemoteQueryServiceImpl 类是此接口的实现,接口方法为接受一种XML请求报文然后返回响应报文。
RegisterContainer 类,在系统初始化时完成不同请求所对应的查询处理类的注册。
QueryFactory 类,根据不同的请求对象返回不同的查询处理类。
ParserRobot 类,负责将XML请求转换为Java请求对象,将Java响应对象转换为XML报文。
QueryProvider 抽象类,是不同种查询处理类的父类。
Request 类,是请求对象的父类。
Response 类,是响应对象的父类。
其中QueryProvider 类,doQuery抽象方法由继承后的子类实现,用来实现不同种报文的查询处理方法,代码为:
/**
* 类说明: <br>
* 创建时间: 2009-11-6 上午10:21:30<br>
*
* @author Seraph<br>
* @email seraph115@gmail.com<br>
*
*/
@Service
public abstract class QueryProvider {
private QueryDaoSupport queryDaoSupport;
private SqlProvider sqlProvider;
public abstract Response doQuery(Request request) throws BrwsException;
public QueryDaoSupport getQueryDaoSupport() {
return queryDaoSupport;
}
public void setQueryDaoSupport(QueryDaoSupport queryDaoSupport) {
this.queryDaoSupport = queryDaoSupport;
}
public SqlProvider getSqlProvider() {
return sqlProvider;
}
public void setSqlProvider(SqlProvider sqlProvider) {
this.sqlProvider = sqlProvider;
}
}
由UML图中,我们可以看到Request , QueryProvider , Response有许多对应的子类,而每个子类都是一种报文类型,也就是系统所能提供的查询服务。所以目前的架构设计下,日后添加一种新报文将很容易,只需要实现一组 Request, QueryProvide及 Response就可以完成新报文的实现,从而达到了松耦合、可扩展的设计。