title: 自下而上的开发实践
date: 2016-10-22 19:23:25
tags:
- 设计模式
- Java
categories: 设计模式
两种开发思路
自上而下:设计先行,根据设计开发
自下而上:开发先行,给予开发重构
关注点分离
类似于文章段落的划分,代码拆分的简单过程可以概括为:
- 分行
- 方法真实逻辑与业务支撑逻辑的划分
- 基于模块层次的划分,而不是语句行数的划分
- 注释
- 良好命名要胜过写大量的注释
- 抽象提取方法
- 合理封装
- 参数内联
- 方法名、可访问性
- 删除不必要的分行与注释
共性与可变性分析
软件工程的两个敌人:
- 共性:开发成本、维护成本
- 可变性:维护成本
共性的复用两种模式:
- 继承
- 强耦合关系,父类的修改影响所有子类
- 单继承限制
- 多态
- 差异式编程
- 钩子方法
- 组合
- 优先使用
- 依赖注入
- 优先组合接口而非实现
- 控制翻转
接口
context+role+collaborator
好处:类型确定,但实现可变、不确定
类与接口的关系类似于人与角色的关系:
- 人可以有多个角色,类可以有多个接口
- 人的某一个角色的确定是由环境决定的,类某一接口的确定是由上下文环境决定的
接口设计应当遵循的原则:接口隔离原则(SIP)
接口应该是干净的,单一的,细粒度的,而不是混合过多的方法,职责单一
扩展式设计的一般过程:
- 分离职责各司其职:关注点分离
- 向上分离:继承
- 向外分离:组合
- 统一接口:
- 提炼接口,提供多态
- 面向接口编程
- 引用接口预留空白
- 依赖注入
以一个事务为例子展示扩展式设计的一般过程:
首先,有一个Service,在方法中业务逻辑与JDBC事务粘合在一起:
public class OrderService {
private DatabasePool dbPool;
public void subscribTrainings(List<Training> trainings,
Customer customer) throws SQLException {
// 初始化方法
Connection c = null;
PreparedStatement ps = null;
Statement s = null;
ResultSet rs = null;
boolean transactionState = false;
try {
// 开启事务
s = c.createStatement();
transactionState = c.getAutoCommit();
c.setAutoCommit(false);
// 业务操作
for (Training training : trainings) {
addTrainingItem(customer, training);
}
addOrder(customer, trainings);
// 提交事务
c.commit();
} catch (SQLException sqlx) {
// 回滚
c.rollback();
throw sqlx;
} finally {
// 关闭操作
try {
c.setAutoCommit(transactionState);
dbPool.release(c);
if (s != null) s.close();
if (ps != null) ps.close();
if (rs != null) rs.close();
} catch (SQLException ignored) {
}
}
}
private void addOrder(Customer customer, List<Training> trainings) {
}
private void addTrainingItem(Customer customer, Training training) {
}
}
第一步在原始代码上分段并添加注释后代码如上所示。对每段代码提取方法,向上抽象:
public class TransactionScope {
private DatabasePool dbPool;
private Connection connection;
private PreparedStatement preparedStatement;
private Statement statement;
private ResultSet resultSet;
private boolean transactionState;
public void using() throws SQLException {
setup();
try {
beginTransaction();
// 留白一个具体的业务的实现:
// 1. (模板方法)继承->钩子方法
// 2. (委派)传递一个方法(方法接口)
commitTransaction();
} catch (SQLException sqlx) {
rollbackTransaction();
throw sqlx;
} finally {
teardown();
}
}
private void rollbackTransaction() throws SQLException {
connection.rollback();
}
private void setup() {
connection = null;
preparedStatement = null;
statement = null;
resultSet = null;
transactionState = false;
}
private void teardown() {
try {
connection.setAutoCommit(transactionState);
dbPool.release(connection);
if (statement != null) statement.close();
if (preparedStatement != null) preparedStatement.close();
if (resultSet != null) resultSet.close();
} catch (SQLException ignored) {
}
}
private void commitTransaction() throws SQLException {
connection.commit();
}
private void beginTransaction() throws SQLException {
statement = connection.createStatement();
transactionState = connection.getAutoCommit();
connection.setAutoCommit(false);
}
}
public interface Command {
void execute();
}
现在提炼出了一个本地事务的基本控制模板,实现了事务控制与业务逻辑控制基本分离。
继续提炼事务控制类的接口,用于横向扩展继承使用:
public interface TransactionScope {
void using(Command command) throws SQLException;
}
public class LocalTransactionScope implements TransactionScope {
@Override
public void using(Command action) throws SQLException {
setup();
try {
beginTransaction();
action.execute(); // 具体的业务逻辑
commitTransaction();
} catch (SQLException sqlx) {
rollbackTransaction();
throw sqlx;
} finally {
teardown();
}
}
}
public class DistructedTransactionScope implements TransactionScope {
@Override
public void using(Command command) throws SQLException {
}
}
调用方通过接口留白,实现依赖注入:
public class OrderService {
private TransactionScope transactionScope;
public void setTransactionScope(TransactionScope transactionScope) {
this.transactionScope = transactionScope;
}
public void subscribTrainings(List<Training> trainings,
Customer customer) throws SQLException {
transactionScope.using(() -> {
for (Training training : trainings) {
addTrainingItem(customer, training);
}
addOrder(customer, trainings);
});
}
}
网友评论