业务背景:sql
根据合同类型的不一样,会有不一样的产品,产品的不一样,有不一样的收入方式。已知每一个合同的合同ID,假设产品类型现有的收入确认方式为(s->a),(w->b),(d->c)数据库
(s->a)表示若是产品类型是s,那么收入确认的方式就是a,依次类推,一共有3中产品设计模式
数据库中的三张表: products 、contracts、revenueRecognitionsbash
代码以说清楚为目的,不会完整写下实例,会用参合伪代码网络
使用过程来组织业务逻辑,每一个过程处理来自表现层的单个请求。做者起名由来:通常一个数据库事务对应一个事务脚本架构
public ResultSet findContract(long contractId){
//将sql 查到的数据集返回 select * from contracts c , products p where id = contractId and c.product = p.id
}
复制代码
public void calculateRevenueRecognitions(long contractId){
ResultSet controcts = findContract(contractId);
String productType = controcts.get("productType");
if( "s".equals(productType) ){
//执行 a 收入确认逻辑代码块
}else if( "w".equals(productType) ){
//执行 b 收入确认逻辑代码块
}else if( "d".equals(productType) ){
//执行 c 收入确认逻辑代码块
}
}
复制代码
在事务脚本中,领域逻辑主要由组织系统所执行的事务来组织。代码结构自己能够以合理的方式模块化。对于多个事务脚本经常使用的组织方式是:模块化
少许逻辑的程序来说,这种实现方式很天然,开发很快,性能以及后期维护理解开销都不大,可是若是业务复杂起来,因为事务脚本自己主要是为了处理一个事务,那么任何公共代码均可能存在多个副本,谨慎的提取公共模块能够解决问题,但再往复杂了去,要更好的组织代码,组织公共模块,须要领域模型性能
合并行为和数据的领域的对象模型ui
class Product{
private StrategyParent strategy;
public static Product newS(){
return new Product(new AStrategy());
}
public static Product newW(){
return new Product(new BStrategy());
}
public static Product newD(){
return new Product(new CStrategy());
}
public void calculateRevenueRecognitions(Contract contract){
strategy.calculateRevenueRecognitions(contract);
}
}
复制代码
每一个策略抽象出来了公共的父类 StrategyParent ,以及对应策略的实现类 AStrategy、BStrategy、CStrategy,这样就能够以插件的形式随时增长不一样的策略就能够影响收入确认的方式this
在合同里面计算收入产品的收入确认
class Contract{
private Product product;
//会有构造方法来传入合同
public void calculateRecognitions(){
product.calculateRevenueRecognitions(this);
}
}
复制代码
class Test{
public static void main(String[] args){
Product s = Product.newS();
Contract c=new Contract(s);
c.calculateRevenueRecognitions();
}
}
复制代码
创建一个完整的由对象组成的层,对目标业务进行领域建模,经过类之间的交互,来完成任务。他有两种风格
对象之间的连续传递,自己就把处理逻辑递交给了“有资格”处理这件事情的对象,自身的调用链就是逻辑链,消除了不少条件判断,也提高了内聚,减小了不一样对象之间的耦合。只是在阅读的时候须要不停的跳转不一样的类来查看逻辑,并且一个领域自己有可能因为自身的业务过多而过于臃肿(实际中臃肿发生几率偏低,建议不要由于臃肿而强行分离,高出一段特殊处理的代码,而产生冗余逻辑,而应该先放到原本就应该在的对象中)
若是业务规则复杂多变,涉及校验、计算、衍生应该用对象模型处理,反之只是作空值判断和少许求和计算,事务脚本是个更好的选择
以一个类对应数据库中的一个表来组织领域逻辑,并且使用单一的类实例包含将对数据进行的各类操做程序。表模块提供了明确的基于方法的接口对数据进行操做
从表中获取数据将数据记录到数据集 DataSet 中它相似于数据库结构
每个表模块 TableModule 都拥有数据集中的一个表 DataTable
class TableModule{
protected DataTable table;
protected TableModule(DataSet ds,String tableName){
table = ds.Tables[tableName];
}
}
复制代码
class Contract extends TableModule{
public Contract(DataSet ds){
super(ds,"contracts");
}
public DataRow thisRowById(long primaryKey){
return table.select("ID = "+primaryKey)[0];
}
}
复制代码
class Contract {
//...
public void calculateRecognitions(long contractId){
DataRow contractRow = thisRowById(contractId);
//多个表模块公用一个数据集
Product prod = new Product(table.DataSet);
//从合同表模块中拿到对应的合同产品数据
long prodId = GetProductId(contractId);
//从产品表模块获取产品的数据类型
String productType = prod.getProductType(prodId);
//执行计算逻辑,逻辑中凡是涉及到须要操做数据的操做,也是经过表模块来完成
if( "s".equals(productType) ){
//执行 a 收入确认逻辑代码块
}else if( "w".equals(productType) ){
//执行 b 收入确认逻辑代码块
}else if( "d".equals(productType) ){
//执行 c 收入确认逻辑代码块
}
}
}
复制代码
表模块将数据与行为封装在一块儿,它能够是一个实例,也能够是一个静态方法的集合。典型的流程是,应用程序首先将数据聚集到一个记录集中,使用该记录集建立一个表模块,若是有多个表模块行为,则一块儿建立,这样表模块就能够在记录集上应用业务逻辑,而后将修改后的记录集传给表现层,表现层处理完后,数据集给表模块校验,并相应存入数据库
与常规对象的关键区别在于它自己没有标识符来表示它所表明的实体对象,一般须要数据库的主键值来查询对应的数据,好比 prodTableModule.getProductType(prodId)
表模块依赖于以表的形式组织数据,适合于使用记录集存取表数据的状况,可是表模块没有提供面向对象能力来组织复杂的领域逻辑,不能在实例之间创建联系
经过一个服务层来定义应用程序边界,在服务层中创建一组可用的操做集合,并在每一个操做内部协调应用程序的响应
public class ReconitionService{
public void calculateRevenueRecognitions(long contractNumber){
Contract contract = Contract.readForUpdate(contractNumber);
contract.calculateRevenueRecognitions();
}
}
复制代码
对于较大的应用,经过垂直企鹅人软件结构将它分红若干个“子系统”,每一个子系统包含切出来的一个部分,每一个子系统能够用该子系统名字来命名,可选的方案包括按照领域模型来划分 ContractService,ProductService 或者是按照程序行为主题来划分 ReconitionService
若是业务逻辑只有一种或者响应不涉及多个事务性资源,就可能不须要服务层,可是只要有多种或多个事务性质资源,则有必要一开始就设计服务层
<企业应用架构模式> 第九章