《重構之美》之改造面向過程式設計
使用面向對象語言進行過程式設計的例子,可謂俯拾皆是。看這段代碼:
- public class SyncExecutor {
 - public void executeSync() {
 - syncSchools();
 - syncGrades();
 - syncFaculties();
 - }
 - }
 
這段代碼很清晰,分別執(zhí)行了對學校、年級與教師信息的同步。一目了然,似乎沒有什么問題。然而,如果深入閱讀各個同步子方法,就會發(fā)現(xiàn)某種壞味道,那就是重復代碼。
- 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);
 - }
 
當然,真實的代碼更加復雜與混亂,但如果經(jīng)過一系列重構,例如Rename Method,Extract Method之后,就會變得逐漸清晰,大體結構如上述展示的代碼。閱讀這樣的代碼,是否發(fā)現(xiàn)各個同步子方法均有似曾相識的感覺呢?究其原因,在于同步的執(zhí)行邏輯大體相似,換言之,它們具有相似的模板。我們需要改善其結構,實現(xiàn)代碼的重用。然而,在方法層面上,我們已很難實現(xiàn)這一點。事實上,當我們在編寫同步方法時,已經(jīng)落入了過程式設計的窠臼。我們首先想到的是執(zhí)行的過程,而非對象?,F(xiàn)在,我們需要將這些執(zhí)行過程封裝為對象,充分地利用繼承等機制實現(xiàn)類級別的重用。顯然,這里可以運用Form Template Method重構。當然,在此之前,我們還需要運用Extract Superclass,對School、Grade等類進行一系列重構,例如為它們建立共同的父類Entity,提供getCode()方法。并運用Rename Method,將原來各自實體類的相關方法,例如getSchoolCode()、getGradeCode()等,更名為getCode()。
現(xiàn)在,我們需要為同步操作定義一個共同的抽象類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對象時,我對原有的代碼實現(xiàn)進行了封裝,因為不同的實體同步類,所要調用的Service對象是不一樣的。因此,需要將調用Service相關方法的實現(xiàn)留給子類?,F(xiàn)在,只需要定義各個同步類繼承DataSynchronizer,重寫相關的受保護抽象方法即可:
- public class SchoolSynchronizer extends DataSynchronizer{}
 - public class GradeSynchronizer extends DataSynchronizer{}
 - public class FacultySynchronizer extends DataSynchronizer{}
 
接著,修改SyncExecutor類的實現(xiàn)。為方便調用同步子類的相關方法,我定義了一個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;
 - }
 - }
 
以真正面向對象的方式來完成上述功能,無論在代碼結構、重用性還是擴展性方面,比諸之前的實現(xiàn),都有了長足的改善。這就是面向對象設計的優(yōu)雅之處。
縱觀整個重構過程,實際上,我在運用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
【編輯推薦】















 
 
 



 
 
 
 