《重構(gòu)之美》之改造面向過(guò)程式設(shè)計(jì)
使用面向?qū)ο笳Z(yǔ)言進(jìn)行過(guò)程式設(shè)計(jì)的例子,可謂俯拾皆是??催@段代碼:
- public class SyncExecutor {
- public void executeSync() {
- syncSchools();
- syncGrades();
- syncFaculties();
- }
- }
這段代碼很清晰,分別執(zhí)行了對(duì)學(xué)校、年級(jí)與教師信息的同步。一目了然,似乎沒有什么問(wèn)題。然而,如果深入閱讀各個(gè)同步子方法,就會(huì)發(fā)現(xiàn)某種壞味道,那就是重復(fù)代碼。
- 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);
- }
當(dāng)然,真實(shí)的代碼更加復(fù)雜與混亂,但如果經(jīng)過(guò)一系列重構(gòu),例如Rename Method,Extract Method之后,就會(huì)變得逐漸清晰,大體結(jié)構(gòu)如上述展示的代碼。閱讀這樣的代碼,是否發(fā)現(xiàn)各個(gè)同步子方法均有似曾相識(shí)的感覺呢?究其原因,在于同步的執(zhí)行邏輯大體相似,換言之,它們具有相似的模板。我們需要改善其結(jié)構(gòu),實(shí)現(xiàn)代碼的重用。然而,在方法層面上,我們已很難實(shí)現(xiàn)這一點(diǎn)。事實(shí)上,當(dāng)我們?cè)诰帉懲椒椒〞r(shí),已經(jīng)落入了過(guò)程式設(shè)計(jì)的窠臼。我們首先想到的是執(zhí)行的過(guò)程,而非對(duì)象。現(xiàn)在,我們需要將這些執(zhí)行過(guò)程封裝為對(duì)象,充分地利用繼承等機(jī)制實(shí)現(xiàn)類級(jí)別的重用。顯然,這里可以運(yùn)用Form Template Method重構(gòu)。當(dāng)然,在此之前,我們還需要運(yùn)用Extract Superclass,對(duì)School、Grade等類進(jìn)行一系列重構(gòu),例如為它們建立共同的父類Entity,提供getCode()方法。并運(yùn)用Rename Method,將原來(lái)各自實(shí)體類的相關(guān)方法,例如getSchoolCode()、getGradeCode()等,更名為getCode()。
現(xiàn)在,我們需要為同步操作定義一個(gè)共同的抽象類DataSynchronizer,然后利用Move Method重構(gòu),將原有SyncExecutor的相關(guān)代碼搬移到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對(duì)象時(shí),我對(duì)原有的代碼實(shí)現(xiàn)進(jìn)行了封裝,因?yàn)椴煌膶?shí)體同步類,所要調(diào)用的Service對(duì)象是不一樣的。因此,需要將調(diào)用Service相關(guān)方法的實(shí)現(xiàn)留給子類?,F(xiàn)在,只需要定義各個(gè)同步類繼承DataSynchronizer,重寫相關(guān)的受保護(hù)抽象方法即可:
- public class SchoolSynchronizer extends DataSynchronizer{}
- public class GradeSynchronizer extends DataSynchronizer{}
- public class FacultySynchronizer extends DataSynchronizer{}
接著,修改SyncExecutor類的實(shí)現(xiàn)。為方便調(diào)用同步子類的相關(guān)方法,我定義了一個(gè)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;
- }
- }
以真正面向?qū)ο蟮姆绞絹?lái)完成上述功能,無(wú)論在代碼結(jié)構(gòu)、重用性還是擴(kuò)展性方面,比諸之前的實(shí)現(xiàn),都有了長(zhǎng)足的改善。這就是面向?qū)ο笤O(shè)計(jì)的優(yōu)雅之處。
縱觀整個(gè)重構(gòu)過(guò)程,實(shí)際上,我在運(yùn)用Convert Procedural Design to Objects重構(gòu)時(shí),大量運(yùn)用了Rename Method、Extract Method、Move Method、Extract Superclass、Form Template Method等重構(gòu)手法。這是合乎常情的。當(dāng)我們?cè)趯?duì)程序進(jìn)行重構(gòu)時(shí),往往需要運(yùn)用各種重構(gòu)手法,才能達(dá)到最終的重構(gòu)目的。對(duì)于大型重構(gòu)而言,這種特征尤其明顯。
原文鏈接:http://www.cnblogs.com/wayfarer/archive/2010/12/23/1914530.html
【編輯推薦】