通过一段时间的看程序和修改别人的程序,简单做了一下总结,自己的一点观点,这也是上班后第一次发表到每月技术文章中的文章,现在贴出来,以后自己回头再看时供自己比较一下。
1. 避免“串起来”
如果一个功能需要多个步骤按次序完成,那么各个分步骤模块儿之间不要发生调用关系,并且按顺序执行的要求也不要由每个分步骤模块儿自己来决定,要交由宿主方法来组织。
示例:
不推荐模式: | 推荐模式: |
functionA(){ rt = functionB(rt); return rt; }
functionB(parameter){ return functionC(rt); }
functionC(parameter){ return rt; }
Void main(){ return functionA(); }
| functionA(){ return rt; }
functionB(parameter){ return rt; }
functionC(parameter){ return rt; }
Void main(){ a = functionA(); b = functionB(a); c = functionC(b); return c; }
|
执行顺序由各函数决定 | 执行顺序由宿主方法决定 |
缺点:模块之间耦合高,不利于代码或宿主方法中功能变动时的维护。 | 优点:各分步骤模块之间偶尔度降低,执行步骤可灵活统一地在宿主方法中被控制。 |
2.遵循“谁创建,谁负责;取用分开”
如果某个操作需要一些对象作为前提条件,那么尽量把这些对象的创建和业务操作中依赖对象的处理逻辑分开。
示例:
典型的数据库操作中会出现这种情况,我们操作数据库表的时候需要先创建一些访问数据库的对象如Connection,然后利用Connection对象去执行我们的sql命令。
我们可以将这个任务划分为几个子任务来处理:
a.创建用于数据库连接的Connection对象;
b.构造sql命令的处理逻辑,调用连接对象执行命令;
c.连接对象的回收;
宿主方法中分别调用这几个模块儿来实现最终的功能。
不推荐模式: | 推荐模式: |
//一个方法完成所有功能 void main(){ Connection conn = null; …//获取连接对象过程 conn = new Connection();
//构造sql并执行、处理结果 Sql = “select * from table”; ResutlSet rs = Conn.execute(sql); …//处理结果
//连接对象销毁 conn.close(); } | //只处理跟业务操作有关的 Void functionF(Connection conn){ //构造sql并执行、处理结果 Sql = “select * from table”; ResutlSet rs = Conn.execute(sql); …//处理结果
}
//宿主方法组织完成功能 Void main(){ //a.创建数据库连接的Connection对象 conn = new Connection();
functionF(conn);//执行
//c.连接对象的回收; …conn回收 } |
缺点:连接对象难以统一管理,代码复用性和可移植性不高。 | 优点:连接对象可以被统一调度管理,减少连接对象只被创建而“忘记”关闭的潜在危险的发生;降低各模块代码的复杂性。
|
3. 数据包装、验证或预处理操作应和业务逻辑分开
如果一个模块有输入或输出数据,但是通常情况下又需要对输入和输出的数据进行格式转换,如:输入数据进行base64格式转换,输出数据包装成json串,那么建议尽量保持业务处理中的输入和输出数据是最原始的,而将数据的预处理和格式转换等放在专门的模块中处理。
示例:
不推荐模式: | 推荐模式: |
Void main(p){ p = …p;//格式转换
…//关于p的业务逻辑
p = …p;//格式转换
return p; } | //公用的转换方法 functionA(p){ rt = …p;//格式转换 return rt; }
Void main(param){ p = functionA(param);
…//关于p的业务逻辑
rt = functionA(p); return rt; } |
缺点:如果格式转化逻辑发生变化,代码将难以维护;各业务逻辑部分的代码重复过多,模块复用率低。 | 优点:能够统一管理数据包转格式;模块复用率提高,代码易于维护。 |