当前位置: 首页 > news >正文

改造面向过程式设计

 《重构之美》之五

使用面向对象语言进行过程式设计的例子,可谓俯拾皆是。看这段代码:

public class SyncExecutor {
public void executeSync() {
syncSchools();
syncGrades();
syncFaculties();
}
}


这段代码很清晰,分别执行了对学校、年级与教师信息的同步。一目了然,似乎没有什么问题。然而,如果深入阅读各个同步子方法,就会发现某种坏味道,那就是重复代码。
private void syncSchools() {
List<School> sourceSchools = getSourceSchools();
List<School> targetSchools = new ArrayList<School>();
List<String> sourceSchoolCodes = getSchoolCodes(sourceSchools);
Map<String,School> targetSchoolWithCodeMapping = schoolService.getSchoolWithCodeMapping(sourceSchoolCodes);
for (School sourceSchool:sourceSchools) {
String schoolCode = sourceSchool.getSchoolCode();
School targetSchool = targetSchoolWithCodeMapping.get(schoolcode);
if (targetSchool == null) {
targetSchool = new School(
sourceSchool.getSchoolCode(),
sourceSchool.getSchoolName(),
sourceSchool.getProvinceCode(),
sourceSchool.getSchoolAddress(),
sourceSchool.getSchoolZip(),
sourceSchool.getSchoolTel());
} else if (isCover) {
targetSchool.setSchoolCode(sourceSchool.getSchoolCode());
targetSchool.setSchoolName(sourceSchool.getSchoolName());
targetSchool.setProvinceCode(sourceSchool.getProvinceCode());
targetSchool.setSchoolAddress(sourceSchool.getSchoolAddress());
targetSchool.setSchoolZip(sourceSchool.getSchoolZip());
targetSchool.setSchoolTel(sourceSchool.getSchoolTel());
}
targetSchools.add(targetSchool);
}

syncService.saveOrUpdate(targetSchools);
}
private void syncGrades() {
List<Grade> sourceGrades = getSourceGrades();
List<Grade> targetGrades = new ArrayList<Grade>();
List<String> sourceGradeCodes = getGradeCodes(sourceGrades);
Map<String,Grade> targetGradeWithCodeMapping = gradeService.getGradeWithCodeMapping(sourceGradeCodes);
for (Grade sourceGrade:sourceGrades) {
String gradeCode = sourceGrade.getGradeCode();
Grade targetGrade = targetGradeWithCodeMapping.get(gradeCode);
if (targetGrade == null) {
targetGrade = new Grade(
sourceGrade.getGradeCode(),
sourceGrade.getName(),
sourceGrade.getEntranceDay(),
sourceGrade.getGraduateDay(),
sourceGrade.getSchoolCode(),
sourceGrade.getSchoolProperty());
} else if (isCover) {
targetGrade.setGradeCode(sourceGrade.getGradeCode());
targetGrade.setName(sourceGrade.getName());
targetGrade.setEntranceDay(sourceGrade.getEntranceDay());
targetGrade.setGraduateDay(sourceGrade.getGraduateDay());
targetGrade.setSchoolCode(sourceGrade.getSchoolCode());
targetGrade.setSchoolProperty(sourceGrade.getSchoolProperty());
}
targetGrades.add(targetGrade);
}

syncService.saveOrUpdate(targetGrades);
}


当然,真实的代码更加复杂与混乱,但如果经过一系列重构,例如Rename Method,Extract Method之后,就会变得逐渐清晰,大体结构如上述展示的代码。阅读这样的代码,是否发现各个同步子方法均有似曾相识的感觉呢?究其原因,在于同步的执行逻辑大体相似,换言之,它们具有相似的模板。我们需要改善其结构,实现代码的重用。然而,在方法层面上,我们已很难实现这一点。事实上,当我们在编写同步方法时,已经落入了过程式设计的窠臼。我们首先想到的是执行的过程,而非对象。现在,我们需要将这些执行过程封装为对象,充分地利用继承等机制实现类级别的重用。显然,这里可以运用Form Template Method重构。当然,在此之前,我们还需要运用Extract Superclass,对School、Grade等类进行一系列重构,例如为它们建立共同的父类Entity,提供getCode()方法。并运用Rename Method,将原来各自实体类的相关方法,例如getSchoolCode()、getGradeCode()等,更名为getCode()。

现在,我们需要为同步操作定义一个共同的抽象类DataSynchronizer,然后利用Move Method重构,将原有SyncExecutor的相关代码搬移到DataSynchronizer中:

public abstract class DataSynchronizer {
public void execute() {
List<Entity> sourceEntities = getSourceEntities();
List<Entity> targetEntities = new ArrayList<Entity>();
List<String> sourceEntityCodes = getEntityCodes(sourceEntities);
Map<String,Entity> targetEntityWithCodeMapping = getEntityWithCodeMapping(sourceEntityCodes);
for (Entity sourceEntity:sourceEntities) {
String entityCode = sourceEntity.getCode();
Entity targetEntity = targetEntityWithCodeMapping.get(entityCode);
if (targetEntity == null) {
targetGrade = createEntity(sourceEntity);
} else if (isCover) {
updateEntity(targetEntity,sourceEntity);
}
targetEntities.add(targetEntity);
}

syncService.saveOrUpdate(targetEntities);
}

protected abstract List<Entity> getSourceEntities();
protected abstract List<String> getEntityCodes(List<Entity> entities);
protected abstract Map<String,Entity> getEntityWithCodeMapping(List<String> entityCodes);
protected abstract Entity createEntity(Entity sourceEntity);
protected abstract void updateEntity(Entity target, Entity source);
}

注意,在获得Entity与Code的Map对象时,我对原有的代码实现进行了封装,因为不同的实体同步类,所要调用的Service对象是不一样的。因此,需要将调用Service相关方法的实现留给子类。现在,只需要定义各个同步类继承DataSynchronizer,重写相关的受保护抽象方法即可:

public class SchoolSynchronizer extends DataSynchronizer{}
public class GradeSynchronizer extends DataSynchronizer{}
public class FacultySynchronizer extends DataSynchronizer{}

接着,修改SyncExecutor类的实现。为方便调用同步子类的相关方法,我定义了一个Factory Method:

public class SyncExecutor {
public void executeSync() {
for (DataSynchronizer dataSync:createSynchronizers()) {
dataSync.execute();
}
}

protected List<DataSynchronizer> createSynchronizers() {
List< DataSynchronizer> synchronizers =
new ArrayList< DataSynchronizer();
synchronizers.add(new SchoolSynchronizer());
synchronizers.add(new GradeSynchronizer());
synchronizers.add(new FacultySynchronizer());

return synchronizers;
}
}

以真正面向对象的方式来完成上述功能,无论在代码结构、重用性还是扩展性方面,比诸之前的实现,都有了长足的改善。这就是面向对象设计的优雅之处。

纵观整个重构过程,实际上,我在运用Convert Procedural Design to Objects重构时,大量运用了Rename Method、Extract Method、Move Method、Extract Superclass、Form Template Method等重构手法。这是合乎常情的。当我们在对程序进行重构时,往往需要运用各种重构手法,才能达到最终的重构目的。对于大型重构而言,这种特征尤其明显。

转载于:https://www.cnblogs.com/wayfarer/archive/2010/12/23/1914530.html

相关文章:

  • 朝三暮四,还是朝四暮三?
  • dell mini9 fluxbox 亮度的调节和 wireless 无线网卡的安装
  • 黑客如何找回被偷走的电脑
  • Microsoft SQL Server Integration Service文章总结
  • 刷机 G7 2.3 Rom 的步骤
  • 毕业后的五年拉开大家差距的原因在哪里(轉)
  • C# 中编译器是如何实现闭包的
  • 积木人生
  • 初探SharePoint 2010如何强化员工协同
  • LoadRunner支持的IE版本
  • 关于ADDRCONF(NETDEV_UP): wlan0: link is not ready(under Monitor mode)
  • 鸡笼山下,帝子台城,振起景阳楼故址;玄武湖边,胭脂古井,依然同泰寺旧观。...
  • 捕获asp.net运行中产生的异常
  • 【原】用shell脚本监视进程,崩溃挂掉后自动重启
  • 社会化媒体营销案例——海尔家电的新浪微博营销
  • [deviceone开发]-do_Webview的基本示例
  • 《剑指offer》分解让复杂问题更简单
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • git 常用命令
  • HTML中设置input等文本框为不可操作
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • Java到底能干嘛?
  • linux学习笔记
  • nfs客户端进程变D,延伸linux的lock
  • ng6--错误信息小结(持续更新)
  • nodejs:开发并发布一个nodejs包
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • SpriteKit 技巧之添加背景图片
  • Unix命令
  • vue学习系列(二)vue-cli
  • 笨办法学C 练习34:动态数组
  • 当SetTimeout遇到了字符串
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 使用SAX解析XML
  • 赢得Docker挑战最佳实践
  • 做一名精致的JavaScripter 01:JavaScript简介
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • Semaphore
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (力扣)循环队列的实现与详解(C语言)
  • (利用IDEA+Maven)定制属于自己的jar包
  • (三)c52学习之旅-点亮LED灯
  • (一)appium-desktop定位元素原理
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (轉)JSON.stringify 语法实例讲解
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .net framework4与其client profile版本的区别
  • .NET 设计模式初探