《重构之美》之改造面向过程式设计

开发 开发工具
我们将看到的是重构之美的改造面向过程式设计问题,希望这些能对大家有所帮助。

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

  1. public class SyncExecutor {   
  2.     public void executeSync() {   
  3.         syncSchools();   
  4.         syncGrades();   
  5.         syncFaculties();   
  6.     }   

这段代码很清晰,分别执行了对学校、年级与教师信息的同步。一目了然,似乎没有什么问题。然而,如果深入阅读各个同步子方法,就会发现某种坏味道,那就是重复代码。

  1. private void syncSchools() {   
  2.     List<School> sourceSchools = getSourceSchools();   
  3.     List<School> targetSchools = new ArrayList<School>();   
  4.     List<String> sourceSchoolCodes = getSchoolCodes(sourceSchools);   
  5.     Map<String,School> targetSchoolWithCodeMapping = schoolService.getSchoolWithCodeMapping(sourceSchoolCodes);   
  6.     for (School sourceSchool:sourceSchools) {   
  7.         String schoolCode = sourceSchool.getSchoolCode();   
  8.         School targetSchool = targetSchoolWithCodeMapping.get(schoolcode);   
  9.         if (targetSchool == null) {   
  10.             targetSchool = new School(   
  11.                 sourceSchool.getSchoolCode(),   
  12.                 sourceSchool.getSchoolName(),   
  13.                 sourceSchool.getProvinceCode(),   
  14.                 sourceSchool.getSchoolAddress(),   
  15.                 sourceSchool.getSchoolZip(),   
  16.                 sourceSchool.getSchoolTel());   
  17.         } else if (isCover) {   
  18.             targetSchool.setSchoolCode(sourceSchool.getSchoolCode());   
  19.             targetSchool.setSchoolName(sourceSchool.getSchoolName());   
  20.             targetSchool.setProvinceCode(sourceSchool.getProvinceCode());   
  21.             targetSchool.setSchoolAddress(sourceSchool.getSchoolAddress());   
  22.             targetSchool.setSchoolZip(sourceSchool.getSchoolZip());   
  23.             targetSchool.setSchoolTel(sourceSchool.getSchoolTel());   
  24.         }   
  25.             targetSchools.add(targetSchool);   
  26.     }  
  27.  
  28.     syncService.saveOrUpdate(targetSchools);   
  29. }   
  30. private void syncGrades() {   
  31.     List<Grade> sourceGrades = getSourceGrades();   
  32.     List<Grade> targetGrades = new ArrayList<Grade>();   
  33.     List<String> sourceGradeCodes = getGradeCodes(sourceGrades);   
  34.     Map<String,Grade> targetGradeWithCodeMapping = gradeService.getGradeWithCodeMapping(sourceGradeCodes);   
  35.     for (Grade sourceGrade:sourceGrades) {   
  36.         String gradeCode = sourceGrade.getGradeCode();   
  37.         Grade targetGrade = targetGradeWithCodeMapping.get(gradeCode);   
  38.         if (targetGrade == null) {   
  39.             targetGrade = new Grade(   
  40.                 sourceGrade.getGradeCode(),   
  41.                sourceGrade.getName(),   
  42.                 sourceGrade.getEntranceDay(),   
  43.                 sourceGrade.getGraduateDay(),   
  44.                 sourceGrade.getSchoolCode(),   
  45. sourceGrade.getSchoolProperty());   
  46.         } else if (isCover) {   
  47.             targetGrade.setGradeCode(sourceGrade.getGradeCode());   
  48.             targetGrade.setName(sourceGrade.getName());   
  49.             targetGrade.setEntranceDay(sourceGrade.getEntranceDay());   
  50.             targetGrade.setGraduateDay(sourceGrade.getGraduateDay());   
  51.             targetGrade.setSchoolCode(sourceGrade.getSchoolCode());   
  52.             targetGrade.setSchoolProperty(sourceGrade.getSchoolProperty());   
  53.         }   
  54. targetGrades.add(targetGrade);   
  55.     }  
  56.  
  57.     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中:

  1. public abstract class DataSynchronizer {   
  2.     public void execute() {   
  3.         List<Entity> sourceEntities = getSourceEntities();   
  4.         List<Entity> targetEntities = new ArrayList<Entity>();   
  5.         List<String> sourceEntityCodes = getEntityCodes(sourceEntities);   
  6.         Map<String,Entity> targetEntityWithCodeMapping = getEntityWithCodeMapping(sourceEntityCodes);   
  7.         for (Entity sourceEntity:sourceEntities) {   
  8.             String entityCode = sourceEntity.getCode();   
  9.             Entity targetEntity = targetEntityWithCodeMapping.get(entityCode);   
  10.             if (targetEntity == null) {   
  11.                 targetGrade = createEntity(sourceEntity);   
  12.             } else if (isCover) {   
  13.                 updateEntity(targetEntity,sourceEntity);   
  14.             }   
  15.             targetEntities.add(targetEntity);   
  16.         }  
  17.  
  18.         syncService.saveOrUpdate(targetEntities);   
  19.     }  
  20.  
  21.     protected abstract List<Entity> getSourceEntities();   
  22.     protected abstract List<String> getEntityCodes(List<Entity> entities);   
  23.     protected abstract Map<String,Entity> getEntityWithCodeMapping(List<String> entityCodes);   
  24.     protected abstract Entity createEntity(Entity sourceEntity);   
  25.     protected abstract void updateEntity(Entity target, Entity source);   

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

  1. public class SchoolSynchronizer extends DataSynchronizer{}   
  2. public class GradeSynchronizer extends DataSynchronizer{}   
  3. public class FacultySynchronizer extends DataSynchronizer{} 

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

  1. public class SyncExecutor {   
  2.     public void executeSync() {   
  3.         for (DataSynchronizer dataSync:createSynchronizers()) {   
  4.             dataSync.execute();   
  5.         }   
  6.     }  
  7.  
  8.     protected List<DataSynchronizer> createSynchronizers() {   
  9.         List< DataSynchronizer> synchronizers =   
  10. new ArrayList< DataSynchronizer();   
  11.         synchronizers.add(new SchoolSynchronizer());   
  12.         synchronizers.add(new GradeSynchronizer());   
  13. synchronizers.add(new FacultySynchronizer());  
  14.  
  15. return synchronizers;   
  16.     }   

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

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

原文链接:http://www.cnblogs.com/wayfarer/archive/2010/12/23/1914530.html

【编辑推荐】

  1. 重构Struts2 JSP分页
  2. 在大型遗留系统基础上运作重构项目
  3. 重构和静态分析被添加进Data Dude工具
  4. 学习笔记 UML面向对象技术概述
  5. C++中的面向对象编程简介
责任编辑:彭凡 来源: 博客园
相关推荐

2011-07-10 16:04:01

程序员

2010-07-15 13:56:24

面向对象面向过程

2009-09-27 14:12:12

面向对象设计单一职责

2021-08-03 08:13:48

重构API代码

2011-03-31 09:32:25

EclipseRefactor

2023-12-08 07:59:41

对象设计设计模式软件设计

2010-08-09 10:39:14

FlexACtionSCrip

2021-07-08 06:08:54

架构重构开发

2013-12-09 13:17:35

命令ping

2021-02-06 08:34:49

函数memoize文档

2019-06-13 11:50:41

Python面向对象编程语言

2021-06-09 09:06:52

Go语言算法

2014-08-28 09:49:56

丽晶软件信息化

2022-08-02 08:07:24

单元测试代码重构

2017-11-28 17:44:28

匠心

2021-08-04 08:56:34

语言Go排序

2011-04-11 10:14:47

HTML 5

2011-07-11 21:54:55

工作站解决方案

2010-07-08 13:35:39

UML面向对象

2011-07-05 14:42:46

java
点赞
收藏

51CTO技术栈公众号