The process-based design example using object-oriented language can be easily described. Read this Code:
Publicclass syncexecutor {
Publicvoid executesync (){
Syncschools ();
Syncgrades ();
Syncfaculties ();
}
}
This code is clear and implements synchronization of school, grade, and teacher information. It seems that there is no problem at a glance. However, if you thoroughly read the sub-Methods of synchronization, you will find a bad taste, that is, repeated code.
Privatevoid 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 ());
} Elseif (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 );
}
Privatevoid 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 ());
} Elseif (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 );
}
Of course, the real code is more complicated and confusing, but after a series of refactoring, such as Rename Method and Extract Method, it will become clearer. The general structure is shown in the code shown above. After reading this code, do you find that each synchronization sub-method feels familiar? The reason is that the execution logic of synchronization is generally similar. In other words, they have similar templates. We need to improve its structure and reuse code. However, at the method level, it is very difficult for us to achieve this. As a matter of fact, when writing a synchronization method, we are already stuck in the process-style design. The first thing we think of is the execution process, not the object. Now, we need to encapsulate these execution processes as objects and make full use of inheritance and other mechanisms to achieve class-Level Reuse. Obviously, Form Template Method can be used for reconstruction. Before that, of course, we also need to use Extract Superclass to reconstruct classes such as School and Grade, for example, to create a common parent class Entity for them and provide the getCode () method. Rename Method is used to Rename the methods related to the original object classes, such as getSchoolCode () and getGradeCode (), to getCode ().
Now, we need to define a common abstract class DataSynchronizer for the synchronization operation, and then use the Move Method to refactor and Move the original SyncExecutor code to DataSynchronizer:
Publicpolicactclass datasynchronizer {
Publicvoid 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 );
} Elseif (isCover ){
UpdateEntity (targetEntity, sourceEntity );
}
TargetEntities. add (targetEntity );
}
SyncService. saveOrUpdate (targetEntities );
}
Protectedabstract List <Entity> getSourceEntities ();
Protectedabstract List <String> getEntityCodes (List <Entity> entities );
Protectedabstract Map <String, Entity> getEntityWithCodeMapping (List <String> entityCodes );
Protectedabstract Entity createEntity (Entity sourceEntity );
Protectedabstractvoid updateEntity (Entity target, Entity source );
}
Note: When obtaining the Map object of Entity and Code, I encapsulate the original Code implementation because the Service objects to be called are different for different object synchronization classes. Therefore, you need to leave the implementation of calling the Service-related method to the subclass. Now, you only need to define the synchronization classes to inherit DataSynchronizer and rewrite the related protected Abstract METHODS:
Publicclass SchoolSynchronizer extends DataSynchronizer {}
Publicclass GradeSynchronizer extends DataSynchronizer {}
Publicclass FacultySynchronizer extends DataSynchronizer {}
Next, modify the implementation of the SyncExecutor class. To facilitate the call of methods related to the synchronization subclass, I defined a Factory Method:
Publicclass SyncExecutor {
Publicvoid executeSync (){
For (DataSynchronizer dataSync: createSynchronizers ()){
DataSync.exe cute ();
}
}
Protected List <DataSynchronizer> createSynchronizers (){
List <DataSynchronizer> synchronizers =
New ArrayList <DataSynchronizer ();
Synchronizers. add (new SchoolSynchronizer ());
Synchronizers. add (new GradeSynchronizer ());
Synchronizers. add (new FacultySynchronizer ());
Return synchronizers;
}
}
The above functions are implemented in an object-oriented way. Both the code structure, reusability, and scalability are greatly improved compared with previous implementations. This is the elegance of object-oriented design.
Throughout the refactoring process, I actually used Rename Method, Extract Method, Move Method, Extract Superclass, Form Template Method, and other refactoring techniques extensively when Convert Procedural Design to Objects refactoring. This is common. When we refactor a program, we often need to use a variety of reconstruction techniques to achieve the final reconstruction goal. For large refactoring, this feature is particularly obvious.