Return to the General Directory
This little program record
1 Move Method
outline
In your program, there is a function that communicates more with another class other than the one it resides in: call the latter, or be or be called.
Create a new function with similar behavior in the class that the function most often refers to. Change the old function into a simple delegate function, or remove the old function completely.
motivation
If one class has too many behaviors, or if one class cooperates too much with another class to form a high degree of coupling, or if another object is used more often than the object in which it resides. Then you have to move the function.
When moving a function, we should decide its moving path according to "which object has more communication with this function".
Example
This refactoring is illustrated by an Acount class that represents an "Account":
class Account { private AccountType _accountType; private int _daysOverdrawn; /// <summary> /// Rules for the Accounting of Overdraft Amount /// </summary> /// <returns></returns> double OverdraftCharge() { if (_accountType.IsPremium()) { double result = 10; if (_daysOverdrawn > 7) { result += (_daysOverdrawn - 7) * 0.85; } return result; } return _daysOverdrawn * 1.75; } double BankCharge() { double result = 4.5; if (_daysOverdrawn > 0) { result += OverdraftCharge(); } return result; } }
The AccountType class is as follows:
class AccountType { public bool IsPremium() { return true; } }
Suppose there are several new accounts, each with its own "overdraft amount accounting rules". So we move Overdraft Charge () to the AccountType class.
The first thing to do is to observe each feature used by Overdraft Charge () and consider whether it is worth moving them with Overdraft Charge (). In this case, we need to leave the _daysOverdraw field in the Account class, because this value does not change with different types of accounts. Then we copy the Overdraft Charge () function code into AccountType and adjust it accordingly.
class AccountType { public double OverdraftCharge(int daysOverdrawn) { if (IsPremium()) { double result = 10; if (daysOverdrawn > 7) { result += (daysOverdrawn - 7) * 0.85; } return result; } return daysOverdrawn * 1.75; } public bool IsPremium() { return true; } }
Then the function ontology of the source function is replaced by a simple delegation action.
class Account {/// <summary> /// Rules for the Accounting of Overdraft Amount /// </summary> /// <returns></returns> double OverdraftCharge() { return _accountType.OverdraftCharge(_daysOverdrawn); } }
This is where the refactoring ends. Of course, we can also delete the source function in Account. We find all the callers of the source function and redirect these calls to call Bank Charge () of Account instead.
class Account { private AccountType _accountType; private int _daysOverdrawn; double BankCharge() { double result = 4.5; if (_daysOverdrawn > 0) { result += _accountType.OverdraftCharge(_daysOverdrawn); } return result; } }
In this case, the moved function refers to only one field, so you just need to pass this field as a parameter to the objective function. If the moved function calls another function in Account, the source object can be passed to the target function.
class AccountType { public double OverdraftCharge(Account account) { if (IsPremium()) { double result = 10; if (daysOverdrawn > 7) { result += (account.GetDaysOverdrawn() - 7) * 0.85; } return result; } return account.GetDaysOverdrawn()* 1.75; } public bool IsPremium() { return true; } }
Summary
When moving functions, check all the features used by source functions in source classes and consider whether they should also be moved. If a feature is only used by the function you want to move, it should be moved together. If other functions use this feature, you can consider moving all functions that use it together.
2 Move Field
outline
In your program, a field is used more by another class than the one in which it resides.
Create a field in the target class, modify all users of the source field, and change them to new fields.
motivation
Moving state and behavior between classes is an indispensable measure in refactoring. As the system evolves, we will find ourselves needing new classes and dragging existing work responsibilities into new classes.
For a field, there are more functions that use it in a class other than the one in which it resides, so consider moving the field.
Example
Take the Account class as an example.
class Account { private AccountType _accountType; private double _interestRate; double GetInterestForAmountByDays(double amount, int days) { return _interestRate * amount * days / 365; } }
We want to move _interestRate to the AccountType class. It has been referenced by several functions, and GetInterestForAmountByDays() is one of them.
We create an _interestRate field in AccountType and encapsulate it as an attribute.
class AccountType { private double _interestRate; public double InterestRate { get => _interestRate; set => _interestRate = value; } }
Now let the function of the _interestRate field accessed in the Account class use the AccountType object instead, and delete the _interestRate field in the Account class.
class Account { private AccountType _accountType; double GetInterestForAmountByDays(double amount, int days) { return _accountType.InterestRate * amount * days / 365; } }
Summary
For C#, perhaps this refactoring technique is better known as "Moving Attributes". Because fields are basically private, attributes are accessible by other functions. Moving attributes is the same as in the example.
To Be Continued......