java代码商的开发模式_面向对象的企业开发(2)事务脚本设计模式代码讲解

注意:以下均为伪代码

前言: 上一篇企业开发设计模式(1)以文字来介绍了事务脚本设计模式。

下面我们来看一下代码讲解:

我们举得例子是,商业系统中常见的收入确认(Revenue Recognition)问题。涉及到何时将所收到的钱入账。比如,我在京东买东西。我可以直接付全款,在收到货物后选择确认收货,这笔钱就会立刻入卖家的账单;也可以打白条,分期24个月,这样,卖家会每个月收到24分之一的货款。

我们的例子是我司卖各种软件:Word、Excel和SQLServerDB。

Word 产品当天立即入账

Excel 当天入账1/3,60天1/3,90天1/3;

DB 当天入账1/3,30天1/3,60天1/3;

以此类推。因此可知收入确认的规则很多。

我们的收入确认模型的类关系文字描述如下:

每个软件产品对应多个合同;

每个合同对应多个收入确认方法,这些确认方法指出何时将收入的不同部分确认。

我们使用两个事务脚本:

一个用来计算合同的总收入确认;

一个用来查询某合同在指定日期前已经确认的收入额。

数据库中有三个表,分别记录着:

产品

合同

收入确认

CREATE TABLE products (ID int primary key, name varchar, type varchar);

CREATE TABLE contracts(ID int primary key, product int, revenue decimal, dateSigned date);

CREATE TABLE revenueRecognitions(contract int ,amount decimal, recognizedOn date, PRIMARY KEY(contract, recogniedOn);

第一个脚本用来计算在指定日期前的确认额。分两步进行:

首先在收入确认表中选择相应的行;

然后相加计算总数。

大多数使用事务脚本的代码都有直接用原生SQL对数据库进行操作。这里我们使用一个简单的表数据入口来封装SQL查询。由于本例逻辑很简单,因此只使用一个数据源入口(GateWay),而不是为每个表设立一个入口。在入口中定义了一个相应的find方法。

class Gateway {

private Connection db;

private static final String findRecognitionsStatement =

"SELECT amount FROM revenueRecognitions WHERE contract=? AND recognizedOn<=?";

public ResultSet findRecognitionsFor(long contractID,MFDate asof) throws SQLException{

PreparedStatement stmt = db.prepareStatement(findRecognitionsStatement);

stmt.setLong(1, contractID);

stmt.setLong(2, asof.toSqlDate());

ResultSet result = stmt.executeQuery();

return result;

}

}

然后在从入口返回结果集,并使用脚本来计算总额:

class RecognitionService {

public Money recognizedRevenue(long contractNumber, MfDate asOf){

Money result = Money.dollars(0);

ResultSet rs = db.findRecognitionFor(contractNumber, MfDate asOf);

while (rs.next()){

result = result.add(Money.dollars(rs.getBugDecimal("amount")));

}

return result;

}

}

第二个脚本用来进行合同的收入确认计算:

表数据入口通过原生SQL实现,以下为合同的查找器方法:

class Gateway {

private Connection db;

private static final String findContractStatement =

"SELECT * FROM contract c, product p WHERE ID=? AND c.products=p.ID";

public ResultSet findRecognitionsFor(long contractID,MFDate asof) throws SQLException{

PreparedStatement stmt = db.prepareStatement(findContractStatement);

stmt.setLong(1, contractID);

ResultSet result = stmt.executeQuery();

return result;

}

}

以下是对数据库插入操作的封装:

class Gateway {

private Connection db;

private static final String insertRecognitionStatement =

"INSERT INSERT revenueRecognitions VALUES (?,?,?)";

public ResultSet findRecognitionsFor(long contractID,Money amount,MFDate asof) {

PreparedStatement stmt = db.prepareStatement(insertRecognitionStatement);

stmt.setLong(1, contractID);

stmt.setBigDecimal(2, amount.amount());

stmt.setDate(3, asof.toSqlDate());

ResultSet result = stmt.executeQuery();

stmt.executeUpdate();

return result;

}

}

以下是用脚本计算一个的合同收入确认:

class RecognitionService {

public void calculateRevenueRecognitions(){

ResultSet contracts = db.findContract(contractNumber);

contract.next();

Money totalRevenue = Money.dollars(contracts.getDate("dateSigned"));

String type = contracts.getString("type");

if (type.equals("Word")){

Money[] allocation = totalRevenue.allocate(3);

db.insertRecognition(contractNumber, allocation[0],recognitionDate);

db.insertRecognition(contractNumber, allocation[1],recognitionDate.addDays(60));

db.insertRecognition(contractNumber, allocation[2],recognitionDate.addDays(90));

} else if (type.equals("Excel")){

...

}

} else if (type.equals("Excel")){

...

}

}

}

在Java系统中,收入确认服务通常是一个POJO类。

当把这个事务脚本的例子与领域模型模式(下一章就要写)中的例子比较时,第一反应就是前者要简单许多,开发速度更快。然而,当系统复杂度增加时,例如收入确认规则涉及许多方面,有很多变体(例如在双十一打5折,国庆节这几天打9折等等)。总之,当事务一旦变得复杂,就很难用事务脚本保持一种低冗余、易维护的设计。而通过,领域模型,引入面向对象设计,便可以使用通用设计模式里的策略模式(strategy pattern)来更换各种增改的收入确定规则。这就是为何 java OO 鼓吹者 宁愿在这种看似简单的情况下依然使用领域模型模式的原因。这也是为何很多py从业者,使用事务脚本模式代码,写出的大量冗余难维护的过程式代码,最终导致周六加班,每晚熬夜,导致脱发的原因。

当然了,对于某些逻辑简单且不会增改需求的场景,事务脚本模式还是应该使用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值