| Slide 1 |
http://www.refactoring.com/catalog/index.html
|
| Slide 4 |
http://www.refactoring.com/catalog/extractMethod.html
You have a code fragment that can be grouped together.
Turn the fragment into a method whose name explains the purpose of the method.
|
| Slide 5 |
http://www.refactoring.com/catalog/inlineMethod.html
A method's body is just as clear as its name.
Put the method's body into the body of its callers and remove the method.
|
| Slide 6 |
http://www.refactoring.com/catalog/inlineTemp.html
You have a temp that is assigned to once with a simple expression, and the temp is getting in the way of other refactorings.
Replace all references to that temp with the expression.
|
| Slide 7 |
http://www.refactoring.com/catalog/replaceTempWithQuery.html
You are using a temporary variable to hold the result of an expression.
Extract the expression into a method. Replace all references to the temp with the expression. The new method can then be used in other methods.
Additional Comments
Side Effects
Paul Haahr pointed out that you can't do this refactoring if the code in between the the assignment to the temp and the use of the temp changes the value of the expression that calculates the temp. In these cases the code is using the temp to snapshot the value of the temp when it's assigned. The name of the temp should convey this fact (and you should change the name if it doesn't).
He also pointed out that it is easy to forget that creating a reference object is a side effect, while creating a value object isn't.
|
| Slide 8 |
http://www.refactoring.com/catalog/introduceExplainingVariable.html
You have a complicated expression.
Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose.
|
| Slide 9 |
http://www.refactoring.com/catalog/splitTemporaryVariable.html
You have a temporary variable assigned to more than once, but is not a loop variable nor a collecting temporary variable.
Make a separate temporary variable for each assignment.
|
| Slide 10 |
http://www.refactoring.com/catalog/removeAssignmentsToParameters.html
The code assigns to a parameter.
Use a temporary variable instead.
|
| Slide 11 |
http://www.refactoring.com/catalog/replaceMethodWithMethodObject.html
You have a long method that uses local variables in such a way that you cannot apply Extract Method
Turn the method into its own object so that all the local variables become fields on that object. You can then decompose the method into other methods on the same object.
Additional Comments
Using a Static Method
You can do this refactoring with a static method, but in this case you don't need the first field pointing back to the original object.
Alternative Steps with Temps
Marnix Klooster suggested an alternative to the mechanics. Rather than creating fields in the Method Object for all the temps, leave the temps as temps and only turn them into fields as you need to in order to use Extract Method. It means you have to use a special form of Extract Method, but may help in reducing the scope of the temps. Of course you can use Reduce Scope of Variableafterwards on any temp that's only used in one method.
|
| Slide 12 |
http://www.refactoring.com/catalog/substituteAlgorithm.html
You want to replace an algorithm with one that is clearer.
Replace the body of the method with the new algorithm.
|
| Slide 14 |
http://www.refactoring.com/catalog/moveMethod.html
A method is, or will be, using or used by more features of another class than the class on which it is defined.
Create a new method with a similar body in the class it uses most. Either turn the old method into a simple delegation, or remove it altogether.
|
| Slide 15 |
http://www.refactoring.com/catalog/moveField.html
A field is, or will be, used by another class more than the class on which it is defined.
Create a new field in the target class, and change all its users.
|
| Slide 16 |
http://www.refactoring.com/catalog/extractClass.html ou have one class doing work that should be done by two.
Create a new class and move the relevant fields and methods from the old class into the new class.
|
| Slide 17 |
http://www.refactoring.com/catalog/inlineClass.html
A class isn't doing very much.
Move all its features into another class and delete it.
|
| Slide 18 |
http://www.refactoring.com/catalog/hideDelegate.html
A client is calling a delegate class of an object.
Create methods on the server to hide the delegate.
|
| Slide 19 |
http://www.refactoring.com/catalog/removeMiddleMan.html
A class is doing too much simple delegation.
Get the client to call the delegate directly.
|
| Slide 20 |
http://www.refactoring.com/catalog/introduceForeignMethod.html
A server class you are using needs an additional method, but you can't modify the class.
Create a method in the client class with an instance of the server class as its first argument.
|
| Slide 21 |
http://www.refactoring.com/catalog/introduceLocalExtension.html
A server class you are using needs several additional methods, but you can't modify the class.
Create a new class that contains these extra methods. Make this extension class a subclass or a wrapper of the original.
Additional Comments
Using Free Functions in C++
--Andy Glew writes:
I comment on this in the light of the recent comp.lang.c++.moderated discussion of using object.method() or free functions, possibly friends, in a class interface. I.e. this comment is more C++ oriented than Java oriented.
Briefly, if the original class used free functions for this sort of interface, the assymmetry introduced by wrapping a class's object.methods() would not exist. In C++
class MfDateSub { private: Date _original; // wrapping the symmetric operator after(date1,date2) public: friend bool after (MfDateSub arg1, MfDateSub arg2) { return after(arg1._original,arg2._original); } public: friend bool after (MfDateSub arg1, Date arg2) { return after(arg1._original,arg2); } public: friend bool after (Date arg1, MfDateSub arg2) { return after(arg1,arg2._original); } }; Now all combinations work; the wrapping is more invisible to the user.
Even better if the free function after(date1,date2) does not require friendship --- although the definition of the extensions gets spread into a few more places.
Note that in the example above, however, an implicit conversion of a MfDateSub value to a Date would suffice.
class MfDateSub { operator Date() const { return _original } } although this approach cannot always work, e.g. when there is extra data to be compared in the extension.
CONCLUSION: using free functions in an interface allows a greater degree of syntactic transparency when wrapping.
Return type for extra methods
--Dmitri Colebatch wrote in to ask why when using the subclass I didn't return the subclass in the extra methods. So I have added the nextDay method to MfDate, but it returns a Date not an MfDate. I can't think of any good reason to return the superclass, so I agree with him and suggest returning MfDate instead. The same is true for the wrapper implementation as well. After all if the client is using the extra method, they are aware of the new class. This way they avoid having to futz with the returned object if they want to invoke other extension methods.
|
| Slide 24 |
http://www.refactoring.com/catalog/selfEncapsulateField.html
You are accessing a field directly, but the coupling to the field is becoming awkward.
Create getting and setting methods for the field and use only those to access the field.
|
| Slide 25 |
http://www.refactoring.com/catalog/replaceDataValueWithObject.html
You have a data item that needs additional data or behavior.
Turn the data item into an object.
|
| Slide 26 |
http://www.refactoring.com/catalog/changeValueToReference.html
You have a class with many equal instances that you want to replace with a single object.
Turn the object into a reference object.
|
| Slide 27 |
http://www.refactoring.com/catalog/changeReferenceToValue.html
You have a reference object that is small, immutable, and awkward to manage.
Turn it into a value object.
|
| Slide 28 |
http://www.refactoring.com/catalog/replaceArrayWithObject.html
You have an array in which certain elements mean different things.
Replace the array with an object that has a field for each element.
|
| Slide 29 |
http://www.refactoring.com/catalog/duplicateObservedData.html
You have domain data available only in a GUI control, and domain methods need access.
Copy the data to a domain object. Set up an observer to synchronize the two pieces of data.
|
| Slide 30 |
http://www.refactoring.com/catalog/changeUnidirectionalAssociationToBidirectional.html
You have two classes that need to use each other's features, but there is only a one-way link.
Add back pointers, and change modifiers to update both sets.
Additional Comments
Doing a remove
In the example I showed an addOrder method, but I didn't show the removeOrder method. If you want to do a remove, you would write it like the add method but set the customer to null.
class Customer {
void removeOrder( Order arg ) {
arg.setCustomer( null );
}
…
}
|
| Slide 31 |
http://www.refactoring.com/catalog/changeBidirectionalAssociationToUnidirectional.html
ou have a two-way association but one class no longer needs features from the other.
Drop the unneeded end of the association.
|
| Slide 32 |
http://www.refactoring.com/catalog/replaceMagicNumberWithSymbolicConstant.html
You have a literal number with a particular meaning.
Create a constant, name it after the meaning, and replace the number with it.
Additional Comments
Using a constant method
For this refactoring I used a symbolic constant, which is the most common Java idiom.
However an alternative is the constant method, which is a method of this form
public static double gravitationalConstant() {
return 9.81;
}
This idiom is less familiar to C based programmers, but is very familiar to Smalltalkers (who didn't have constants in their language). On the whole I don't tend to use this in Java as it is less idiomatic to the language. However if you need to replace the simple return with a calculated value then it's worth changing the constant field to a constant method. (I guess there should be a refactoring for that....)
|
| Slide 33 |
http://www.refactoring.com/catalog/encapsulateField.html
There is a public field.
Make it private and provide assessors.
|
| Slide 34 |
http://www.refactoring.com/catalog/encapsulateCollection.html
A method returns a collection.
Make it return a read-only view and provide add/remove methods.
|
| Slide 35 |
http://www.refactoring.com/catalog/replaceRecordWithDataClass.html
You need to interface with a record structure in a traditional programming environment.
Make a dumb data object for the record.
|
| Slide 36 |
http://www.refactoring.com/catalog/replaceTypeCodeWithClass.html
A class has a numeric type code that does not affect its behavior.
Replace the number with a new class.
Corrections
Privitizing the accessors for the type code
At the end of the refactoring I said that you could make those methods that use the type code, eggetCode(), private. I neglected to say that you first have to find the callers of those methods and change them to no longer use the code number.
|
| Slide 37 |
http://www.refactoring.com/catalog/replaceTypeCodeWithSubclasses.html
You have an immutable type code that affects the behavior of a class.
Replace the type code with subclasses.
|
| Slide 38 |
http://www.refactoring.com/catalog/replaceTypeCodeWithStateStrategy.html
You have a type code that affects the behavior of a class, but you cannot use subclassing.
Replace the type code with a state object.
|
| Slide 39 |
http://www.refactoring.com/catalog/replaceSubclassWithFields.html
You have subclasses that vary only in methods that return constant data.
Change the methods to superclass fields and eliminate the subclasses.
|
| Slide 41 |
http://www.refactoring.com/catalog/decomposeConditional.html
You have a complicated conditional (if-then-else) statement.
Extract methods from the condition, then part, and else parts.
|
| Slide 42 |
http://www.refactoring.com/catalog/consolidateConditionalExpression.html
You have a sequence of conditional tests with the same result.
Combine them into a single conditional expression and extract it.
|
| Slide 43 |
http://www.refactoring.com/catalog/consolidateDuplicateConditionalFragments.html
The same fragment of code is in all branches of a conditional expression.
Move it outside of the expression.
Additional Comments
Using with try/catch blocks
Paul Haahr rightly took me to task for being far too glib when I talked about consolidating with exceptions. You can only pull repeated code into a final block if all non-fatal exceptions are caught. This is because the finally block is executed after any exception, including those that don't have a catch clause. (Of course you may prefer that to happen, but that's not a semantics preserving change.)
|
| Slide 44 |
http://www.refactoring.com/catalog/removeControlFlag.html
You have a variable that is acting as a control flag for a series of boolean expressions.
Use a break or return instead.
|
| Slide 45 |
http://www.refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html
A method has conditional behavior that does not make clear what the normal path of execution is
Use Guard Clauses for all the special cases
|
| Slide 46 |
http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
You have a conditional that chooses different behavior depending on the type of an object.
Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract.
|
| Slide 47 |
http://www.refactoring.com/catalog/introduceNullObject.html
You have repeated checks for a null value.
Replace the null value with a null object.
|
| Slide 48 |
http://www.refactoring.com/catalog/introduceAssertion.html
A section of code assumes something about the state of the program.
Make the assumption explicit with an assertion.
|
| Slide 49 |
關於介面(Interface)
參數列合理化:不多、不少剛好是使用端要的
穩定的介面很好,但適合的介面更重要:不要擔心調整介面 signature,舊的介面用 @depurated
萃取出資料泥團(Data Clump),讓介面更清楚表達自身的實質內涵
不斷強化測試,尤其是對介面的測試
介面的定義表達的一個人的溝通能力
|
| Slide 50 |
關於介面(Interface)
參數列合理化:不多、不少剛好是使用端要的
穩定的介面很好,但適合的介面更重要:不要擔心調整介面 signature,舊的介面用 @depurated
萃取出資料泥團(Data Clump),讓介面更清楚表達自身的實質內涵
不斷強化測試,尤其是對介面的測試
介面的定義表達的一個人的溝通能力
|
| Slide 51 |
http://www.refactoring.com/catalog/renameMethod.html
The name of a method does not reveal its purpose.
Change the name of the method.
|
| Slide 52 |
http://www.refactoring.com/catalog/addParameter.html
A method needs more information from its caller.
Add a parameter for an object that can pass on this information.
|
| Slide 53 |
http://www.refactoring.com/catalog/removeParameter.html
A parameter is no longer used by the method body.
Remove it.
|
| Slide 54 |
http://www.refactoring.com/catalog/separateQueryFromModifier.html
You have a method that returns a value but also changes the state of an object.
Create two methods, one for the query and one for the modification.
|
| Slide 55 |
http://www.refactoring.com/catalog/parameterizeMethod.html
Several methods do similar things but with different values contained in the method body.
Create one method that uses a parameter for the different values.
Corrections
Clarifying the Mechanics
I've since (after a suggestion from Prof Kahlbrandt) found better mechanics for this refactoring (changes are in bold)
Create a parameterized method that can be substituted for each repetitive methodCompileReplace the body of one method with a call to the new methodCompile and TestUse Inline Method on the old methodRepeat for all the methods.
|
| Slide 56 |
http://www.refactoring.com/catalog/replaceParameterWithExplicitMethods.html
You have a method that runs different code depending on the values of an enumerated parameter.
Create a separate method for each value of the parameter.
|
| Slide 57 |
http://www.refactoring.com/catalog/preserveWholeObject.html
You are getting several values from an object and passing these values as parameters in a method call.
Send the whole object instead.
|
| Slide 58 |
http://www.refactoring.com/catalog/replaceParameterWithMethod.html
An object invokes a method, then passes the result as a parameter for a method. The receiver can also invoke this method.
Remove the parameter and let the receiver invoke the method.
|
| Slide 59 |
http://www.refactoring.com/catalog/introduceParameterObject.html
You have a group of parameters that naturally go together.
Replace them with an object.
Additional Comments
Dealing with a chain of calls
Ralph Johnson pointed out to me that a common case isn't clear in the Refactoring book. This case is when you have a bunch of methods that call each other, all of which have a clump of parameters that need this refactoring. In this case you don't want to apply Introduce Parameter Object because it would lead to lots of new objects when you only want to have one object that's passed around.
The approach to use is to start with the head of the call chain and apply Introduce Parameter Object there. Then apply Preserve Whole Object to the other methods.
|
| Slide 60 |
http://www.refactoring.com/catalog/removeSettingMethod.html
A field should be set at creation time and never altered.
Remove any setting method for that field.
Corrections
Using Final
Paul Haahr kindly pointed out to me that I've not been accurate with the use of final in this refactoring. The point that I missed is that you can't use a final field if the initialization is done in a separate method to the constructor. This the example at the bottom of page 301 (that uses initialize(id)) just does not compile. (My apologies for not testing that fragment, some do slip through the net....) If you want to use a separate method, you can't make the field final
This also means that the mechanics is incorrect. Making the field final should be the last step not the first, and can only be done if it's possible.
|
| Slide 61 |
http://www.refactoring.com/catalog/hideMethod.html
A method is not used by any other class.
Make the method private.
|
| Slide 62 |
http://www.refactoring.com/catalog/replaceConstructorWithFactoryMethod.html
You want to do more than simple construction when you create an object.
Replace the constructor with a factory method.
Additional Comments
Dimitri Paltchoun pointed out that as well as using Class.forName() and a string for a client to specify the created class, you can also use the class object itself. This would lead you to method like
static Employee create(Class c){
try{
return (Employee)c.newInstance();
}catch(Exception e){
throw new IllegalException("Unable to instantiate" +c);
}
}
This would be called from this code
Employee.create(Engineer.class);
|
| Slide 63 |
http://www.refactoring.com/catalog/encapsulateDowncast.html
A method returns an object that needs to be downcasted by its callers.
Move the downcast to within the method.
|
| Slide 64 |
http://www.refactoring.com/catalog/replaceErrorCodeWithException.html
A method returns a special code to indicate an error.
Throw an exception instead.
|
| Slide 65 |
http://www.refactoring.com/catalog/replaceExceptionWithTest.html
You are throwing an exception on a condition the caller could have checked first.
Change the caller to make the test first.
|
| Slide 68 |
http://www.refactoring.com/catalog/pullUpField.html
Two subclasses have the same field.
Move the field to the superclass.
|
| Slide 69 |
http://www.refactoring.com/catalog/pullUpMethod.html
You have methods with identical results on subclasses.
Move them to the superclass.
|
| Slide 70 |
http://www.refactoring.com/catalog/pullUpConstructorBody.html
You have constructors on subclasses with mostly identical bodies.
Create a superclass constructor; call this from the subclass methods.
|
| Slide 71 |
http://www.refactoring.com/catalog/pushDownMethod.html
Behavior on a superclass is relevant only for some of its subclasses.
Move it to those subclasses.
|
| Slide 72 |
http://www.refactoring.com/catalog/pushDownField.html
A field is used only by some subclasses.
Move the field to those subclasses.
|
| Slide 73 |
http://www.refactoring.com/catalog/extractSubclass.html
A class has features that are used only in some instances.
Create a subclass for that subset of features.
|
| Slide 74 |
http://www.refactoring.com/catalog/extractSuperclass.html
You have two classes with similar features.
Create a superclass and move the common features to the superclass.
|
| Slide 75 |
http://www.refactoring.com/catalog/extractInterface.html
Several clients use the same subset of a class's interface, or two classes have part of their interfaces in common.
Extract the subset into an interface.
|
| Slide 76 |
http://www.refactoring.com/catalog/collapseHierarchy.html
A superclass and subclass are not very different.
Merge them together.
|
| Slide 77 |
http://www.refactoring.com/catalog/formTemplateMethod.html
You have two methods in subclasses that perform similar steps in the same order, yet the steps are different.
Get the steps into methods with the same signature, so that the original methods become the same. Then you can pull them up.
|
| Slide 78 |
http://www.refactoring.com/catalog/replaceInheritanceWithDelegation.html
A subclass uses only part of a superclasses interface or does not want to inherit data.
Create a field for the superclass, adjust methods to delegate to the superclass, and remove the subclassing.
|
| Slide 79 |
http://www.refactoring.com/catalog/replaceDelegationWithInheritance.html
You're using delegation and are often writing many simple delegations for the entire interface.
Make the delegating class a subclass of the delegate.
|
| Slide 1 |
Catalog Of Refactoring SOURCE FROM: http://www.refactoring.com/
報告人:高明權
DATE :2008/11/25 Ver 1.00
EMAIL : MCK6214@gmail.com
|
| Slide 2 |
Catalog Of Refactoring
Composing Methods (#9)
Moving Features Between Objects (#8)
Organizing Data (#16)
Simplifying Conditional Expression (#8)
Making Method Calls Simpler (#15)
Dealing with Generalization (#12)
2
資料來源: http://www.refactoring.com/
|
| Slide 3 |
Composing Methods
Extract Method
Inline Method
Inline Temp
Replace Temp with Query
Introduce Explaining Variable
Split Temporary Variable
Remove Assignments to Parameters
Replace Method with Method Object
Substitute Algorithm
3
|
| Slide 4 |
Extract Method
Before
void printOwing() {
printBanner();
//print details
System.out.println ("name: " + _name);
System.out.println ("amount " + getOutstanding());
}
After
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails (double outstanding) {
System.out.println ("name: " + _name);
System.out.println ("amount " + outstanding);
}
4
You have a code fragment that can be grouped together
|
| Slide 5 |
Inline Method
Before
int getRating() {
return (moreThanFiveLateDeliveries()) ? 2 : 1;
}
boolean moreThanFiveLateDeliveries() {
return _numberOfLateDeliveries > 5;
}
After
int getRating() {
return (_numberOfLateDeliveries > 5) ? 2 : 1;
}
5
A method's body is just as clear as its name.
|
| Slide 6 |
Inline Temp
Before
double basePrice = anOrder.basePrice();
return (basePrice > 1000);
After
return (anOrder.basePrice() > 1000) ;
6
You have a temp that is assigned to once with a simple expression, and the temp is getting in the way of other refactorings.
|
| Slide 7 |
Replace Temp with Query
Before
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98;
After
if (basePrice() > 1000)
return basePrice() * 0.95;
else
return basePrice() * 0.98;
...
double basePrice() {
return _quantity * _itemPrice;
}
7
You are using a temporary variable to hold the result of an expression.
|
| Slide 8 |
Introduce Explaining Variable
Before
if ( (platform.toUpperCase().indexOf("MAC") > -1) &&
(browser.toUpperCase().indexOf("IE") > -1) &&
wasInitialized() && resize > 0 ) {
// do something
}
After
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIEBrowser && wasResized && wasInitialized() ) {
// do something
}
8
You have a complicated expression.
|
| Slide 9 |
Split Temporary Variable
Before
double temp = 2 * (_height + _width);
System.out.println (temp);
temp = _height * _width;
System.out.println (temp);
After
final double perimeter = 2 * (_height + _width);
System.out.println (perimeter);
final double area = _height * _width;
System.out.println (area);
9
You have a temporary variable assigned to more than once, but is not a loop variable nor a collecting temporary variable.
|
| Slide 10 |
Remove Assignments to Parameters
Before
int discount (int inputVal, int quantity, int yearToDate) {
if (inputVal > 50) inputVal -= 2;
...
}
After
int discount (int inputVal, int quantity, int yearToDate) {
int result = inputVal;
if (inputVal > 50) result -= 2;
...
}
10
The code assigns to a parameter.
|
| Slide 11 |
Replace Method with Method Object
Before
class Order {
double price() {
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// long computation;
...
}
...
}
After
11
You have a long method that uses local variables in such a way that you cannot apply Extract Method
|
| Slide 12 |
Substitute Algorithm
Before
String foundPerson(String[] people){
for (int i = 0; i < people.length; i++) {
if (people[i].equals ("Don")){
return "Don";
}
if (people[i].equals ("John")){
return "John";
}
if (people[i].equals ("Kent")){
return "Kent";
}
}
return "";
}
After
String foundPerson(String[] people){
List candidates = Arrays.asList(new String[] {"Don", "John", "Kent"});
for (int i=0; i<people.length; i++)
if (candidates.contains(people[i]))
return people[i];
return "";
}
12
You want to replace an algorithm with one that is clearer.
|
| Slide 13 |
Moving Features Between Objects
Move Method
Move Field
Extract Class
Inline Class
Hide Delegate
Remove Middle Man
Introduce Foreign Method
Introduce Local Extension
13
|
| Slide 14 |
Move Method
Before
class Project {
Person[] participants;
}
class Person {
int id;
boolean participate(Project p) {
for(int i=0; i<p.participants.length; i++) {
if (p.participants[i].id == id) return(true);
}
return(false);
}
}
... if (x.participate(p)) ...
After
class Project {
Person[] participants;
boolean participate(Person x) {
for(int i=0; i<participants.length; i++) {
if (participants[i].id == x.id) return(true);
}
return(false);
}
}
class Person {
int id;
}
... if (p.participate(x)) ...
14
A method is, or will be, using or used by more features of another class than the class on which it is defined.
|
| Slide 15 |
Move Field
Before
After
15
A field is, or will be, used by another class more than the class on which it is defined.
|
| Slide 16 |
Extract Class
Before
After
16
You have one class doing work that should be done by two.
|
| Slide 17 |
Inline Class
Before
After
17
A class isn't doing very much.
|
| Slide 18 |
Hide Delegate
Before
After
18
A client is calling a delegate class of an object.
|
| Slide 19 |
Remove Middle Man
Before
After
19
A class is doing too much simple delegation.
|
| Slide 20 |
Introduce Foreign Method
Before
Date newStart = new Date(previousEnd.getYear(),
previousEnd.getMonth(),
previousEnd.getDate() + 1);
After
Date newStart = nextDay(previousEnd);
private static Date nextDay(Date arg) {
return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1);
}
20
A server class you are using needs an additional method, but you can't modify the class.
|
| Slide 21 |
Introduce Local Extension
Before
After
21
A server class you are using needs several additional methods, but you can't modify the class.
|
| Slide 22 |
Organizing Data 1/2
Self Encapsulate Field
Replace Data Value with Object
Change Value to Reference
Change Reference to Value
Replace Array with Object
Duplicate Observed Data
Change Unidirectional Association to Bidirectional
Change Bidirectional Association to Unidirectional
22
|
| Slide 23 |
Organizing Data 2/2
Replace Magic Number with Symbolic Constant
Encapsulate Field
Encapsulate Collection
Replace Record with Data Class
Replace Type Code with Class
Replace Type Code with Subclasses
Replace Type Code with State/Strategy
Replace Subclass with Fields
23
|
| Slide 24 |
Self Encapsulate Field
Before
private int _low, _high;
boolean includes (int arg) {
return arg >= _low && arg <= _high;
}
After
private int _low, _high;
boolean includes (int arg) {
return arg >= getLow() && arg <= getHigh();
}
int getLow() {
return _low;
}
int getHigh() {
return _high;
}
24
You are accessing a field directly, but the coupling to the field is becoming awkward.
|
| Slide 25 |
Replace Data Value with Object
Before
After
25
You have a data item that needs additional data or behavior.
|
| Slide 26 |
Change Value to Reference
Before
After
26
You have a class with many equal instances that you want to replace with a single object.
|
| Slide 27 |
Change Reference to Value
Before
After
27
You have a reference object that is small, immutable, and awkward to manage.
|
| Slide 28 |
Replace Array with Object
Before
String[] row = new String[3];
row [0] = "Liverpool";
row [1] = "15";
After
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");
28
You have an array in which certain elements mean different things.
|
| Slide 29 |
Duplicate Observed Data
Before
After
29
You have domain data available only in a GUI control, and domain methods need access.
|
| Slide 30 |
Change Unidirectional Association to Bidirectional
Before
After
30
You have two classes that need to use each other's features, but there is only a one-way link.
|
| Slide 31 |
Change Bidirectional Association to Unidirectional
Before
After
31
You have a two-way association but one class no longer needs features from the other.
|
| Slide 32 |
Replace Magic Number with Symbolic Constant
Before
double potentialEnergy(double mass, double height) {
return mass * height * 9.81;
}
After
double potentialEnergy(double mass, double height) {
return mass * GRAVITATIONAL_CONSTANT * height;
}
static final double GRAVITATIONAL_CONSTANT = 9.81;
32
You have a literal number with a particular meaning.
|
| Slide 33 |
Encapsulate Field
Before
public String _name;
After
private String _name;
public String getName() {
return _name;
}
public void setName(String arg) {
_name = arg;
}
33
There is a public field.
|
| Slide 34 |
Encapsulate Collection
Before
After
34
A method returns a collection.
|
| Slide 35 |
Replace Record with Data Class
Before
After
35
You need to interface with a record structure in a traditional programming environment.
|
| Slide 36 |
Replace Type Code with Class
Before
After
36
A class has a numeric type code that does not affect its behavior.
|
| Slide 37 |
Replace Type Code with Subclasses
Before
After
37
You have an immutable type code that affects the behavior of a class.
|
| Slide 38 |
Replace Type Code with State/Strategy
Before
After
38
You have a type code that affects the behavior of a class, but you cannot use subclassing.
|
| Slide 39 |
Replace Subclass with Fields
Before
After
39
You have subclasses that vary only in methods that return constant data.
|
| Slide 40 |
Simplifying Conditional Expression
Decompose Conditional
Consolidate Conditional Expression
Consolidate Duplicate Conditional Fragments
Remove Control Flag
Replace Nested Conditional with Guard Clauses
Replace Conditional with Polymorphism
Introduce Null Object
Introduce Assertion
40
|
| Slide 41 |
Decompose Conditional
Before
if (date.before (SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;
else
charge = quantity * _summerRate;
After
if (notSummer(date))
charge = winterCharge(quantity);
else
charge = summerCharge(quantity);
41
You have a complicated conditional (if-then-else) statement.
|
| Slide 42 |
Consolidate Conditional Expression
Before
double disabilityAmount() {
if (_seniority < 2) return 0;
if (_monthsDisabled > 12) return 0;
if (_isPartTime) return 0;
// compute the disability amount
...
}
After
double disabilityAmount() {
if (isNotEligableForDisability()) return 0;
// compute the disability amount
...
}
42
You have a sequence of conditional tests with the same result.
|
| Slide 43 |
Consolidate Duplicate Conditional Fragments
Before
if (isSpecialDeal()) {
total = price * 0.95;
send();
} else {
total = price * 0.98;
send();
}
After
if (isSpecialDeal())
total = price * 0.95;
else
total = price * 0.98;
send();
43
The same fragment of code is in all branches of a conditional expression.
|
| Slide 44 |
Remove Control Flag
Before
After
44
You have a variable that is acting as a control flag for a series of boolean expressions.
|
| Slide 45 |
Replace Nested Conditional with Guard Clauses
Before
double getPayAmount() {
double result;
if (_isDead) result = deadAmount();
else {
if (_isSeparated)
result = separatedAmount();
else {
if (_isRetired) result = retiredAmount();
else result = normalPayAmount();
};
}
return result;
};
After
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};
45
A method has conditional behavior that does not make clear what the normal path of execution is
|
| Slide 46 |
Replace Conditional with Polymorphism
Before
double getSpeed() {
switch (_type) {
case EUROPEAN:
return getBaseSpeed();
case AFRICAN:
return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;
case NORWEGIAN_BLUE:
return (_isNailed) ? 0 : getBaseSpeed(_voltage);
}
throw new RuntimeException ("Should be unreachable");
}
After
46
You have a conditional that chooses different behavior depending on the type of an object.
|
| Slide 47 |
Introduce Null Object
Before
if (customer == null)
plan = BillingPlan.basic();
else
plan = customer.getPlan();
After
47
You have repeated checks for a null value.
|
| Slide 48 |
Introduce Assertion
Before
double getExpenseLimit() {
// should have either expense limit or a primary project
return (_expenseLimit != NULL_EXPENSE) ?
_expenseLimit:
_primaryProject.getMemberExpenseLimit();
}
After
double getExpenseLimit() {
Assert.isTrue (_expenseLimit != NULL_EXPENSE || _primaryProject != null);
return (_expenseLimit != NULL_EXPENSE) ?
_expenseLimit:
_primaryProject.getMemberExpenseLimit();
}
48
A section of code assumes something about the state of the program.
|
| Slide 49 |
Making Method Calls Simpler 1/2
Rename Method
Add Parameter
Remove Parameter
Separate Query from Modifier
Parameterize Method
Replace Parameter with Explicit Methods
Reserve Whole Object
Replace Parameter with Method
49
|
| Slide 50 |
Making Method Calls Simpler 2/2
Introduce Parameter Object
Remove Setting Method
Hide Method
Replace Constructor with Factory Method
Encapsulate Downcast
Replace Error Code with Exception
Replace Exception with Test
50
|
| Slide 51 |
Rename Method
Before
After
51
The name of a method does not reveal its purpose.
|
| Slide 52 |
Add Parameter
Before
After
52
A method needs more information from its caller.
|
| Slide 53 |
Remove Parameter
Before
After
53
A parameter is no longer used by the method body.
|
| Slide 54 |
Separate Query from Modifier
Before
After
54
You have a method that returns a value but also changes the state of an object.
|
| Slide 55 |
Parameterize Method
Before
After
55
Several methods do similar things but with different values contained in the method body.
|
| Slide 56 |
Replace Parameter with Explicit Methods
Before
void setValue (String name, int value) {
if (name.equals("height")) {
_height = value;
return;
}
if (name.equals("width")) {
_width = value;
return;
}
Assert.shouldNeverReachHere();
}
After
void setHeight(int arg) {
_height = arg;
}
void setWidth (int arg) {
_width = arg;
}
56
You have a method that runs different code depending on the values of an enumerated parameter.
|
| Slide 57 |
Reserve Whole Object
Before
int low = daysTempRange().getLow();
int high = daysTempRange().getHigh();
withinPlan = plan.withinRange(low, high);
After
withinPlan = plan.withinRange(daysTempRange());
57
You are getting several values from an object and passing these values as parameters in a method call.
|
| Slide 58 |
Replace Parameter with Method
Before
int basePrice = _quantity * _itemPrice;
discountLevel = getDiscountLevel();
double finalPrice = discountedPrice (basePrice, discountLevel);
After
int basePrice = _quantity * _itemPrice;
double finalPrice = discountedPrice (basePrice);
58
An object invokes a method, then passes the result as a parameter for a method. The receiver can also invoke this method.
|
| Slide 59 |
Introduce Parameter Object
Before
After
59
You have a group of parameters that naturally go together.
|
| Slide 60 |
Remove Setting Method
Before
After
60
A field should be set at creation time and never altered.
|
| Slide 61 |
Hide Method
Before
After
61
A method is not used by any other class.
|
| Slide 62 |
Replace Constructor with Factory Method
Before
Employee (int type) {
_type = type;
}
After
static Employee create(int type) {
return new Employee(type);
}
62
You want to do more than simple construction when you create an object.
|
| Slide 63 |
Encapsulate Downcast
Before
Object lastReading() {
return readings.lastElement();
}
After
Reading lastReading() {
return (Reading) readings.lastElement();
}
63
A method returns an object that needs to be downcasted by its callers.
|
| Slide 64 |
Replace Error Code with Exception
Before
int withdraw(int amount) {
if (amount > _balance)
return -1;
else {
_balance -= amount;
return 0;
}
}
After
void withdraw(int amount) throws BalanceException {
if (amount > _balance) throw new BalanceException();
_balance -= amount;
}
64
A method returns a special code to indicate an error.
|
| Slide 65 |
Replace Exception with Test
Before
double getValueForPeriod (int periodNumber) {
try {
return _values[periodNumber];
} catch (ArrayIndexOutOfBoundsException e) {
return 0;
}
}
After
double getValueForPeriod (int periodNumber) {
if (periodNumber >= _values.length) return 0;
return _values[periodNumber];
}
65
You are throwing an exception on a condition the caller could have checked first.
|
| Slide 66 |
Dealing with Generalization 1/2
Pull Up Field
Pull Up Method
Pull Up Constructor Body
Push Down Method
Push Down Field
Extract Subclass
Extract Superclass
Extract Interface
66
|
| Slide 67 |
Dealing with Generalization 2/2
Collapse Hierarchy
Form Template Method
Replace Inheritance with Delegation
Replace Delegation with Inheritance
67
|
| Slide 68 |
Pull Up Field
Before
After
68
Two subclasses have the same field.
|
| Slide 69 |
Pull Up Method
Before
After
69
You have methods with identical results on subclasses.
|
| Slide 70 |
Pull Up Constructor Body
Before
class Manager extends Employee...
public Manager (String name, String id, int grade) {
_name = name;
_id = id;
_grade = grade;
}
...
}
After
class Manager extends Employee...
public Manager (String name, String id, int grade) {
super (name, id);
_grade = grade;
}
...
}
70
You have constructors on subclasses with mostly identical bodies.
|
| Slide 71 |
Push Down Method
Before
After
71
Behavior on a superclass is relevant only for some of its subclasses.
|
| Slide 72 |
Push Down Field
Before
After
72
…
|
| Slide 73 |
Extract Subclass
Before
After
73
A class has features that are used only in some instances.
|
| Slide 74 |
Extract Superclass
Before
After
74
You have two classes with similar features.
|
| Slide 75 |
Extract Interface
Before
After
75
Several clients use the same subset of a class's interface, or two classes have part of their interfaces in common.
|
| Slide 76 |
Collapse Hierarchy
Before
After
76
A superclass and subclass are not very different.
|
| Slide 77 |
Form Template Method
Before
After
77
You have two methods in subclasses that perform similar steps in the same order, yet the steps are different.
|
| Slide 78 |
Replace Inheritance with Delegation
Before
After
78
A subclass uses only part of a superclasses interface or does not want to inherit data.
|
| Slide 79 |
Replace Delegation with Inheritance
Before
After
79
You're using delegation and are often writing many simple delegations for the entire interface.
|
| Slide 80 |
善用但不濫用重構!
|