It is important to clarify the position of the class where the function resides. This will prevent your class from being too much coupled with other classes. It also makes the cohesion of your class more secure and makes your entire system more uncluttered. Simply put, if in your program, the function of a class is more in the process of interacting with other classes, calling the latter or being called by the latter, then you have to be aware that you are going to decide whether the class is really suitable for the class in which he was originally located.
To put it simply, the trick is to create a new function with similar behavior in the new class that the function most commonly references, so that the old function becomes a simple delegate function or is completely erased.
The Move method is the backbone of the refactoring theory. If a class has too much responsibility, or if a class and other classes have too much co-operation, coupling is too high, you need to consider the move method to make the class simple. You need to browse through your own classes and see if the responsibilities of the class are too many, and the degree of coupling with other classes. Of course, if you do move field, you should also do such a check, because we all know that the function is related to the field, the field is moved by you, the function must be checked again (if you need to move both field and move Method, generally first move Filed will be relatively simple). Once you have found such a function, you have to analyze his caller and the callee, and if there is polymorphism, you should also consider the function that he redefined in the inheritance system to determine the final moving path.
If you're not sure if you should move a function, you might want to move this function, and you can switch to other functions, and moving other functions tends to be simpler than the decision you make. If you find that you are still unable to determine whether or not to move the function after you have moved other functions, it is not important that the function move or move at this time.
Practice:
- Examine all the attributes (fields and functions) used by the source function in the source class, and consider whether they should also be moved together. If an attribute (function or field) is only used by the function you want to move, you can move it together. If you find that other functions in the source class also use this feature, you should consider moving all functions that use these features together, and sometimes moving a set of functions is easier than a move function.
- Check the subclass and superclass of the source class to see if there are other declarations for the function. If there are other declarations, this function is polymorphic, unless your target class also presents the same inheritance system, you may not be able to move.
- In the target class declaration of this function, the interface name can also be different, by you according to the context of the grasp.
- Copy the code of the source function into the target function, adjust the code, for example, if the target function uses the attributes (fields and functions) in the source class, you have to decide how to get the target function to the source object, if the target class does not have the corresponding reference mechanism, you should consider whether to add the parameter table and pass the source object directly. If you use an exception at this point, you should also consider whether the exception should be placed in the source class or in the target class.
- Compiles the target class.
- Decide how to refer to your new target function from the source function, if there is a ready-made field or function to get you to the target object that is certainly best, if not, consider whether it is possible to build a function to get the target class object, if it is still difficult, You should consider whether you should create a new field for the source class to specifically save the target class object, and don't worry if it will be a permanent modification, because a later refactoring project might allow you to delete these fields.
- Modify the source function so that he becomes a simple delegate function to invoke the target function.
- Compile, test.
- If you often refer to the target function in the source class, it is obviously easier to leave the source function interface to make it a simple delegate function.
- If you want to delete the source function, replace the call to the source function in the source class with the call to the target function. You can compile the test every time you modify a point, or you can make a batch modification by using your editor and then compile the test, which may be easier.
- Compile, test.
Example:
classAccount ...DoubleOverdraftcharge () {if(M_type.ispremium ()) {Doubleresult =Ten; if(M_daysoverdrawn >7) {result+ = (M_daysoverdrawn-7) *0.85; returnresult; } Else { returnM_daysoverdrawn *1.75; } }}DoubleBankcharge () {Doubleresult =4.5; if(M_daysoverdrawn >0) {result+=Overdraftcharge (); returnresult; }}Private: AccountType m_type; intM_daysoverdrawn;
We found that the different arithmetic rules would be different depending on the type, suggesting that we should put the function in the class of change, that should be put in Accoounttype. First, we look at the move function, observe the source class attribute used in it, and find that only m_daysoverdrawn is the attribute of the source class, but we do not choose to move it to the target class but instead place it in the source class, because we can conclude that he does not change with the type of account. Note: This technique is still important, putting things that don't change in a fixed class, moving something that's easy to change into the corresponding change class, and allowing us to refactor later to do better polymorphic processing. For this refactoring to be simple, we can pass the arguments directly to the target function. Next, we create a new function in the AccountType and name it, where we can choose the same name, and then paste the function code all over it, and make the corresponding changes.
classaccounttype ...DoubleOverdraftcharge (intdaysoverdrawn) { if(Ispremium ()) {Doubleresult =Ten; if(Daysoverdrawn >7) {result+ = (Daysoverdrawn-7) *0.85; returnresult; } Else { returnDaysoverdrawn *1.75; } }}
Turn your own ispremium () into a direct call, replacing your own field in the source class in the form of a parameter. There are four ways to do this when we need the nature of the source class.
- Move this feature into the target class as well.
- Establish or use a reference from the target class to the source class
- Passing the source object as a parameter to the target function
- If the desired attribute is just a variable, pass it as a parameter to the target function
In this example we use the fourth way, simply passing the variable parameter to the target function. Adjust the target function to make it through the compilation we make the source function modification, let him become a simple delegate, because we already exist in the Source class AccountType field, so it is very simple to change, after the completion of the compilation test.
class Account ... Double Overdraftcharge () { return m_type.overdraftcharge (m_daysoverdrawn);}
We can keep the source function like this can also be deleted, if you want to delete you have to do one more step, is to find all the caller of the source function, and then to replace, such as the account of the Bankcharge () function to use this source function to replace the corresponding After the substitution is complete, you can delete the declaration of the source function.
Double Bankcharge () { double4.5; if 0 ) { + = m_type.overdraftcharge (m_daysoverdrawn) ; return result;} }
If the removed function is not private, you must check if any other classes are using this function, because C + + is a strongly typed language, you can simply delete the declaration of the source function and let the compiler help you find it.
Because in this case a field of the source class is used, we can simply give the target function in the form of a parameter, and if the source function uses another function or multiple fields of the source class, we must pass the source class object to the target function
classaccounttype ...DoubleOverdraftcharge (Account *Account ) { if(Ispremium ()) {Doubleresult =Ten; if(Account->daysoverdrawn () >7) {result+ = (Account->daysoverdrawn ()-7) *0.85; returnresult; } Else { returnAccount->daysoverdrawn () *1.75; } }}
For example, if we want to get daysoverdrawn through other functions of the source class or require multiple attributes of the source class, then we must pass the source class object in the past to make the call in the target function. If the target function requires too many attributes of the source class, then you need to refactor it further. Typically, you need to extract method and move a portion back to the source class.
"Refactoring – Improving the design of existing code" reading notes----Move Method