Presentation is loading. Please wait.

Presentation is loading. Please wait.

Introduction to Refactoring

Similar presentations


Presentation on theme: "Introduction to Refactoring"— Presentation transcript:

1 Introduction to Refactoring
9/21/2018

2 Software Entropy the tendency for software to disorder is equal to (design integrity)^(-1) 9/21/2018

3 Reasons for software deterioration
During the maintenance phase new features are added bugs are repaired the program is adapted to work with new devices, servers, and platforms. maintenance is not done by the original developers 9/21/2018

4 Deterioration symptoms
Methods become longer. Methods logic become more convoluted. Classes become bloated. Classes become irrelevant. Replicated code appears. Parallel inheritance hierarchies develop. Comments pile up like red flags. 9/21/2018

5 Anti Patterns Bloated Class Duplicated Code Inappropriate Intimacy
A class with too many responsibilities. Every system change seems to require a change to this class. Bloated Class Duplicated Code Inappropriate Intimacy Incomplete Library Class Lazy Class Duplicated code can be the result of too many copy and paste jobs. Class A knows too much about the private details of class B. A library class that we can't modify doesn't have all of the features we wish it had. A class doesn't have enough responsibilities. 9/21/2018

6 Long Method A method contains too many lines of code. Generally, any method longer than ten lines should make you start asking questions. Like the Hotel California, something is always being added to a method but nothing is ever taken out. Since it is easier to write code than to read it, this "smell" remains unnoticed until the method turns into an ugly, oversized beast. Mentally, it is often harder to create a new method than to add to an existing one: "But it's just two lines, there's no use in creating a whole method just for that..." Which means that another line is added and then yet another, giving birth to a tangle of spaghetti code. In addition, long methods offer the perfect hiding place for unwanted duplicate code. 9/21/2018

7 Treatment As a rule of thumb, if you feel the need to comment on something inside a method, you should take this code and put it in a new method. Even a single line can and should be split off into a separate method, if it requires explanations. And if the method has a descriptive name, nobody will need to look at the code to see what it does. 9/21/2018

8 Payoff Among all types of object oriented code, classes with short methods live longest. The longer a method or function is, the harder it becomes to understand and maintain it. 9/21/2018

9 Large Class A class contains many fields/methods/lines of code.
Classes usually start small. But over time, they get bloated as the program grows. As is the case with long methods as well, programmers usually find it mentally less taxing to place a new feature in an existing class than to create a new class for the feature. 9/21/2018

10 Treatment When a class is wearing too many (functional) hats, think about splitting it up. 9/21/2018

11 Pay off Refactoring of these classes spares developers from needing to remember a large number of attributes for a class. In many cases, splitting large classes into parts avoids duplication of code and functionality. 9/21/2018

12 Primitive Obsession Use of primitives instead of small objects for simple tasks (such as currency, ranges, special strings for phone numbers, etc.) Use of constants for coding information (such as a constant USER_ADMIN_ROLE = 1 for referring to users with administrator rights.) Use of string constants as field names for use in data arrays. 9/21/2018

13 Reasons for the Problem
Like most other smells, primitive obsessions are born in moments of weakness. "Just a field for storing some data!" the programmer said. Creating a primitive field is so much easier than making a whole new class, right? And so it was done. Then another field was needed and added in the same way. Lo and behold, the class became huge and unwieldy. Primitives are often used to "simulate" types. So instead of a separate data type, you have a set of numbers or strings that form the list of allowable values for some entity. Easy-to-understand names are then given to these specific numbers and strings via constants, which is why they are spread wide and far. Another example of poor primitive use is field simulation. The class contains a large array of diverse data and string constants (which are specified in the class) are used as array indices for getting this data. 9/21/2018

14 Treatment If you have a large variety of primitive fields, it may be possible to logically group some of them into their own class. Even better, move the behavior associated with this data into the class too. For this task, try Replace Data Value with Object. 9/21/2018

15 Pay Off Code becomes more flexible thanks to use of objects instead of primitives. Better understandability and organization of code. Operations on particular data are in the same place, instead of being scattered. No more guessing about the reason for all these strange constants and why they are in an array. Easier finding of duplicate code. 9/21/2018

16 9/21/2018

17 Long Parameter List More than three or four parameters for a method.
A long list of parameters might happen after several types of algorithms are merged in a single method. A long list may have been created to control which algorithm will be run and how. Long parameter lists may also be the byproduct of efforts to make classes more independent of each other. For example, the code for creating specific objects needed in a method was moved from the method to the code for calling the method, but the created objects are passed to the method as parameters. Thus the original class no longer knows about the relationships between objects, and dependency has decreased. But if several of these objects are created, each of them will require its own parameter, which means a longer parameter list. It is hard to understand such lists, which become contradictory and hard to use as they grow longer. Instead of a long list of parameters, a method can use the data of its own object. If the current object does not contain all necessary data, another object (which will get the necessary data) can be passed as a method parameter. 9/21/2018

18 Treatment Check what values are passed to parameters. If some of the arguments are just results of method calls of another object, use Replace Parameter with Method Call. This object can be placed in the field of its own class or passed as a method parameter. Instead of passing a group of data received from another object as parameters, pass the object itself to the method, by using Preserve Whole Object. If there are several unrelated data elements, sometimes you can merge them into a single parameter object via Introduce Parameter Object. 9/21/2018

19 Pay Off More readable, shorter code.
Refactoring may reveal previously unnoticed duplicate code. Do not get rid of parameters if doing so would cause unwanted dependency between classes. 9/21/2018

20 behavior(Pi+1) = behavior(Pi) entropy(Pi+1) < entropy(Pi)
Refactoring Process Refactoring means improving the design of existing code. The idea is to gradually apply refactoring transformations to the original program, P0: behavior(Pi+1) = behavior(Pi) entropy(Pi+1) < entropy(Pi) 9/21/2018

21 Anti Patterns vs Refactoring
Extract Class Extract Subclass Replace Data Value with Object Bloated Class Duplicated Code Inappropriate Intimacy Incomplete Library Class Lazy Class Extract Method Extract Class Pull Up method Move Method Replace Bi-directional Association with Unidirectional Replace Inheritance with Delegation Hide Delegation Introduce Foreign Method Introduce Local Extension Inline Class Collapse Hierarchy 9/21/2018

22 Replace Inheritance with Delegation
Extract Method Long Method Middle Man Refused Bequest Speculative Generality Switch Statements Remove Middle Man Inline Method Replace Delegation with Inheritance Replace Inheritance with Delegation Collapse Hierarchy Inline Class Replace Conditional with Polymorphism Replace Type Tags with Subclasses 9/21/2018

23 Three levels of refactoring
Block-Level Refactorings Method-Level Refactorings Class-Level Refactorings 9/21/2018

24 Composing Methods A large part of refactoring is composing methods to package code properly. Almost all the time the problems come from methods that are too long. Long methods are troublesome because they often contain lots of information, which gets buried by the complex logic that usually gets dragged in. 9/21/2018

25 The purpose of design is to try to anticipate future requirements.
As a rule of thumb, anything over half a page is too long. Ideally, methods should be so short and so well-named, that reading the code isn't even necessary. Long Method Middle Man Refused Bequest Speculative Generality Switch Statements Generally, the middleman serves a purpose. In some cases, however, the middleman looses its purpose and only seems to stand in the way between the client and the delegate. It's okay for subclass B to override some of the methods it inherits from superclass A, but taken to the extreme, one begins to wonder if B should be a subclass of A. Long methods are difficult to understand and maintain. As a rule of thumb, anything over half a page is too long. Another rule of thumb: instead of inserting a comment to explain a task within a long method, think about extracting the task into a well-named method. Ideally, methods should be so short and so well-named, that reading the code isn't even necessary. A middleman forwards requests from the client to a delegate object. Generally, the middleman serves a purpose. It translates the request into the language of the delegate, it sends the request over a machine boundary, it handles some requests by itself, it manages memory on behalf of the delegate, etc. In some cases, however, the middleman looses its purpose and only seems to stand in the way between the client and the delegate. The purpose of design is to try to anticipate future requirements. Taken to an extreme, this can result in the Biped superclass of Employee, the planet field of Address, or the "jump to hyperspace" method of Airplane. Object-Oriented Programming is about data-driven control. Programmers shouldn't be in the business of specifying the flow of control. Instead, programmers should be in the business of specifying data. The data should decide the flow of control. The purpose of design is to try to anticipate future requirements. Object-Oriented Programming is about data-driven control. Programmers shouldn't be in the business of specifying the flow of control. 9/21/2018

26 Extract Method Problem:
You have a code fragment that can be grouped together. Solution: Turn the fragment into a method whose name explains the purpose of the method. Summary: 你有一段代码可以被组织在一起并独立出来。 Mechanism: 将这段代码放进一个独立函数中,并让函数名称解释该函数的用途。 如果每个函数的粒度都很小,那么函数之间彼此复用的机会就更大; 这会使高层函数读起来就像一系列注释; 如果函数都是细粒度,那么函数的覆写也会更容易些。 9/21/2018

27 System.out.println ("name: " + getName());
void printOwing() { printBanner(); //print details System.out.println ("name: " + getName()); System.out.println ("amount " + getOutstanding()); } printDetails(getName(), getOutstanding()); void printDetails (string name, double outstanding) System.out.println ("name: " + name); System.out.println ("amount " + outstanding); 9/21/2018

28 9/21/2018

29 Mechanics Create a new method, and name it after the intention of the method. Copy the extracted code from the source method into the new target method. Scan the extracted code for references to any variables that are local in scope to the source method. These are local variables and parameters to the method. See whether any temporary variables are used only within this extracted code. If so, declare them in the target method as temporary variables. 9/21/2018

30 Compile when you have dealt with all the locally-scoped variables.
Look to see whether any of these local-scope variables are modified by the extracted code. Pass into the target method as parameters local-scope variables that are read from the extracted code. Compile when you have dealt with all the locally-scoped variables. Replace the extracted code in the source method with a call to the target method. Compile and test. Look to see whether any of these local-scope variables are modified by the extracted code. If one variable is modified, see whether you can treat the extracted code as a query and assign the result to the variable concerned. If this is awkward, or if there is more than one such variable, you can't extract the method as it stands. You may need to use Split Temporary Variable and try again. You can eliminate temporary variables with Replace Temp with Query (see the discussion in the examples). 9/21/2018

31 Example void printOwing() { Enumeration e = _orders.elements();
double outstanding = 0.0; // print banner System.out.println ("**************************"); System.out.println ("***** Customer Owes ******"); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } //print details System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); 9/21/2018

32 void printOwing() { Enumeration e = _orders
void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } //print details System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); void printBanner() { // print banner System.out.println ("**************************"); System.out.println ("***** Customer Owes ******"); 9/21/2018

33 void printOwing() { Enumeration e = _orders
void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } printDetails(outstanding); void printDetails (double outstanding) { System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); 9/21/2018

34 void printOwing() { printBanner(); double outstanding = getOutstanding(); printDetails(outstanding); } double getOutstanding() { Enumeration e = _orders.elements(); double result = 0.0; while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); result += each.getAmount(); return result; 9/21/2018

35 void printOwing(double previousAmount) { Enumeration e = _orders
void printOwing(double previousAmount) { Enumeration e = _orders.elements(); double outstanding = previousAmount * 1.2; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } printDetails(outstanding); 9/21/2018

36 void printOwing(double previousAmount) { double outstanding = previousAmount * 1.2; printBanner(); outstanding = getOutstanding(outstanding); printDetails(outstanding); } double getOutstanding(double initialValue) { double result = initialValue; Enumeration e = _orders.elements(); while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); result += each.getAmount(); return result; 9/21/2018

37 Inline Method Problem: A method's body is just as clear as its name.
Solution: Put the method's body into the body of its callers and remove the method. 一个函数,如果其本体(method body)与名称(method name)同样清楚易懂。 则在函数调用点插入函数本体,然后去除该函数。 间接层有其价值,但不是所有的间接层都有价值。间接性可能会带来帮助,但不必要的间接性总是令人不舒服。 9/21/2018

38 return (moreThanFiveLateDeliveries()) ? 2 : 1; }
int getRating() { return (moreThanFiveLateDeliveries()) ? 2 : 1; } boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; int getRating() { return (_numberOfLateDeliveries > 5) ? 2 : 1; } 9/21/2018

39 Tower of Pisa Tower of Pisa
9/21/2018

40 Mechanics Check that the method is not polymorphic.
Don't inline if subclasses override the method; they cannot override a method that isn't there. Find all calls to the method. Replace each call with the method body. Compile and test. Remove the method definition. 1检查函数,确定它不具有多态性 如果subclass继承了这个函数,就不要将此函数inline化,因为subclass无法覆盖一个根本不存在的函数。 2找出这个函数的所有被调用点。 3将这个函数的所有被调用点都替换为函数本体。 4编译,测试。 5删除该函数的定义。 9/21/2018

41 Inline Temp Problem: You have a temp that is assigned to once with a simple expression. Solution: Replace all references to that temp with the expression. 有一个临时变量,只被一个简单表达式赋值,而它妨碍了其他重构手法。 将所有对该变量的引用动作,替换为对它賦值的那个表达式自身。 9/21/2018

42 double basePrice = anOrder.basePrice(); return (basePrice > 1000);
return (anOrder.basePrice() > 1000); 9/21/2018

43 Mechanics Declare the temp as final if it isn't already, and compile.
Find all references to the temp and replace them with the right-hand side of the assignment. Compile and test after each change. Remove the declaration and the assignment of the temp. Compile and test. 1、如果這個臨時變量並未被聲明爲 final,那就將它聲明爲 final,然後編譯。 這可以檢查該臨時變量是否真的只被賦值一次。 2、找到該臨時變量的所有引用點,將它們替換為「為臨時變量賦值」之語句中的等號右側表達式。 3、每次修改後,編譯並測試。 4、修改完所有引用點之後,删除該臨時變量的聲明式和賦值語句。 5、編譯,測試。 9/21/2018

44 Replace temp with query
Problem: You are using a temporary variable to hold the result of an expression. Solution: 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. 9/21/2018

45 Mechanics Look for a temporary variable that is assigned to once.
Declare the temp as final. Compile Extract the right-hand side of the assignment into a method. Initially mark the method as private. Ensure the extracted method is free of side effects Compile and test. Use Replace Temp with method on the temp. 1、找出只被賦值一次的臨時變量。 如果某個臨時變量被賦值超過一次,考慮使用 Split Temporary Variable將它分割成多個變量。 2、將該臨時變量聲明爲 final。 3、編譯(這可確保該臨時變量的確只被賦值一次) 4、將「對該臨時變量賦值」之語句的等號右側部分提煉到一個獨立函數中。 首先將函數聲明爲 private。日後你可能會發現有更多 class 需要使用它,彼時你可輕易放鬆對它的保護。 確保提煉出來的函數無任何連帶影響(副作用),也就是說該函數並不修改任何對象內容。如果它有連帶影響,就對它進行 Separate Query from Modifier。 5、編譯,測試。 6、在該臨時變量身上實施 Inline Temp。 9/21/2018

46 Example double getPrice() { int basePrice = _quantity * _itemPrice;
double discountFactor; if (basePrice > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; } 9/21/2018

47 final int basePrice = _quantity * _itemPrice;
double getPrice() { final int basePrice = _quantity * _itemPrice; final double discountFactor; if (basePrice > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; } 9/21/2018

48 final int basePrice = getBasePrice(); final double discountFactor;
double getPrice() { final int basePrice = getBasePrice(); final double discountFactor; if (basePrice > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; } private int getBasePrice() { return _quantity * _itemPrice; 9/21/2018

49 final int basePrice = getBasePrice(); final double discountFactor;
double getPrice() { final int basePrice = getBasePrice(); final double discountFactor; if (getBasePrice() > 1000) discountFactor = 0.95; else discountFactor = 0.98; return getBasePrice() * discountFactor; } private int getBasePrice() { return _quantity * _itemPrice; 9/21/2018

50 final double discountFactor; if (getBasePrice() > 1000)
double getPrice() { final double discountFactor; if (getBasePrice() > 1000) discountFactor = 0.95; else discountFactor = 0.98; return getBasePrice() * discountFactor; } private int getBasePrice() { return _quantity * _itemPrice; 9/21/2018

51 final double discountFactor = getDiscountFactor();
double getPrice() { final double discountFactor = getDiscountFactor(); return basePrice() * discountFactor; } private double getDiscountFactor() { if (basePrice() > 1000) return 0.95; else return 0.98; 9/21/2018

52 double getPrice() { return getBasePrice()
double getPrice() { return getBasePrice() * getDiscountFactor(); } private double getDiscountFactor() { if (basePrice() > 1000) return 0.95; else return 0.98; private int getBasePrice() { return _quantity * _itemPrice; 9/21/2018

53 Introduce explaining variable
Problem: You have a complicated expression. Solution: Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose. 9/21/2018

54 if ((platform. toUpperCase(). indexOf("MAC") > -1) && (browser
if ((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0 ) { // do something } final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1; final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1; final boolean wasResized = resize > 0; if (isMacOs && isIEBrowser && wasInitialized() && wasResized) { 9/21/2018

55 Motivation Expressions can become very complex and hard to read. In such situations temporary variables can be helpful to break down the expression into something more manageable. Introduce Explaining Variable is particularly valuable with conditional logic in which it is useful to take each clause of a condition and explain what the condition means with a well-named temp. Another case is a long algorithm, in which each step in the computation can be explained with a temp. 9/21/2018

56 Mechanics Declare a final temporary variable, and set it to the result of part of the complex expression. Replace the result part of the expression with the value of the temp. If the result part of the expression is repeated, you can replace the repeats one at a time. Compile and test. Repeat for other parts of the expression. 1、找出只被賦值一次的臨時變量。 如果某個臨時變量被賦值超過一次,考慮使用 Split Temporary Variable將它分割成多個變量。 2、將該臨時變量聲明爲 final。 3、編譯(這可確保該臨時變量的確只被賦值一次) 4、將「對該臨時變量賦值」之語句的等號右側部分提煉到一個獨立函數中。 首先將函數聲明爲 private。日後你可能會發現有更多 class 需要使用它,彼時你可輕易放鬆對它的保護。 確保提煉出來的函數無任何連帶影響(副作用),也就是說該函數並不修改任何對象內容。如果它有連帶影響,就對它進行 Separate Query from Modifier。 5、編譯,測試。 6、在該臨時變量身上實施 Inline Temp。 9/21/2018

57 Example double price() {
// price is base price - quantity discount + shipping return _quantity * _itemPrice - Math.max(0, _quantity - 500) * _itemPrice * Math.min(_quantity * _itemPrice * 0.1, 100.0); } 9/21/2018

58 // price is base price - quantity discount + shipping
double price() { // price is base price - quantity discount + shipping final double basePrice = _quantity * _itemPrice; return basePrice - Math.max(0, _quantity - 500) * _itemPrice * Math.min(_quantity * _itemPrice * 0.1, 100.0); } 9/21/2018

59 // price is base price - quantity discount + shipping
double price() { // price is base price - quantity discount + shipping final double basePrice = _quantity * _itemPrice; return basePrice - Math.max(0, _quantity - 500) * _itemPrice * Math.min(basePrice * 0.1, 100.0); } 9/21/2018

60 // price is base price - quantity discount + shipping
double price() { // price is base price - quantity discount + shipping final double basePrice = _quantity * _itemPrice; final double quantityDiscount = Math.max(0, _quantity - 500) * _itemPrice * 0.05; return basePrice - quantityDiscount + Math.min(basePrice * 0.1, 100.0); } 9/21/2018

61 final double basePrice = _quantity * _itemPrice;
double price() { final double basePrice = _quantity * _itemPrice; final double quantityDiscount = Math.max(0, _quantity - 500) * _itemPrice * 0.05; final double shipping = Math.min(basePrice * 0.1, 100.0); return basePrice - quantityDiscount + shipping; } 9/21/2018

62 Example Using Extract Method
double price() { // price is base price - quantity discount + shipping return _quantity * _itemPrice - Math.max(0, _quantity - 500) * _itemPrice * Math.min(_quantity * _itemPrice * 0.1, 100.0); } 9/21/2018

63 return basePrice() - quantityDiscount() + shipping(); }
double price() { return basePrice() - quantityDiscount() + shipping(); } private double quantityDiscount() { return Math.max(0, _quantity - 500) * _itemPrice * 0.05; private double shipping() { return Math.min(basePrice() * 0.1, 100.0); private double basePrice() { return _quantity * _itemPrice; 9/21/2018

64 Split Temporary Variable
Problem: You have a temporary variable assigned to more than once, but is not a loop variable nor a collecting temporary variable. Solution: Make a separate temporary variable for each assignment. double temp = 2 * (_height + _width); System.out.println (temp); temp = _height * _width; final double perimeter = 2 * (_height + _width); System.out.println (perimeter); final double area = _height * _width; System.out.println (area); 9/21/2018

65 Motivation Temporary variables are made for various uses.
Sometimes a temp is being assigned to several times such as loop and collecting variables Other times temps are used to hold the result of a long-winded bit of code for easy reference later. These kinds of variables should be set only once. That they are set more than once is a sign that they have more than one responsibility within the method. Any variable with more than one responsibility should be replaced with a temp for each responsibility. Using a temp for two different things is very confusing for the reader. 9/21/2018

66 Mechanics Change the name of a temp at its declaration and its first assignment. If the later assignments are of the form i = i + some expression, that indicates that it is a collecting temporary variable, so don't split it. The operator for a collecting temporary variable usually is addition, string concatenation, writing to a stream, or adding to a collection. Declare the new temp as final. Change all references of the temp up to its second assignment. Declare the temp at its second assignment. Compile and test. Repeat in stages, each stage renaming at the declaration, and changing references until the next assignment. 9/21/2018

67 Example double getDistanceTravelled (int time) { double result; double acc = _primaryForce / _mass; int primaryTime = Math.min(time, _delay); result = 0.5 * acc * primaryTime * primaryTime; int secondaryTime = time - _delay; if (secondaryTime > 0) { double primaryVel = acc * _delay; acc = (_primaryForce + _secondaryForce) / _mass; result += primaryVel * secondaryTime * acc * secondaryTime * secondaryTime; } return result; } 9/21/2018

68 double getDistanceTravelled (int time) { double result; final double primaryAcc = _primaryForce / _mass; int primaryTime = Math.min(time, _delay); result = 0.5 * primaryAcc * primaryTime * primaryTime; int secondaryTime = time - _delay; if (secondaryTime > 0) { double primaryVel = primaryAcc * _delay; double acc = (_primaryForce + _secondaryForce) /_mass; result += primaryVel * secondaryTime * acc * secondaryTime * secondaryTime; } return result; } 9/21/2018

69 double getDistanceTravelled (int time) { double result; final double primaryAcc = _primaryForce / _mass; int primaryTime = Math.min(time, _delay); result = 0.5 * primaryAcc * primaryTime * primaryTime; int secondaryTime = time - _delay; if (secondaryTime > 0) { double primaryVel = primaryAcc * _delay; final double secondaryAcc = (_primaryForce + _secondaryForce) / _mass; result += primaryVel * secondaryTime * secondaryAcc * secondaryTime * secondaryTime; } return result; } 9/21/2018

70 Remove Assignments to Parameters
Problem: The code assigns to a parameter. Solution: Use a temporary variable instead. int discount (int inputVal, int quantity, int yearToDate) { if (inputVal > 50) inputVal -= 2; ... int discount (int inputVal, int quantity, int yearToDate) { int result = inputVal; if (inputVal > 50) result -= 2; ... 9/21/2018

71 Motivation The phrase "assigns to a parameter" means that if you pass in an object named foo, in the parameter, assigning to the parameter means to change foo to refer to a different object. There is no problems with doing something to the object that was passed in since that is done all the time. Changing foo to refer to another object entirely is not preferred in Java. void aMethod(Object foo) {     foo.modifyInSomeWay();    // that's OK     foo = anotherObject;    // trouble and despair will follow you     ... 9/21/2018

72 Java uses pass by value exclusively, and this is based on that usage.
With pass by value, any change to the parameter is not reflected in the calling routine. It is much clearer if you use only the parameter to represent what has been passed in, because that is a consistent usage. In Java, don't assign to parameters, and if you see code that does, apply Remove Assignments to Parameters. Of course this rule does not necessarily apply to other languages that use output parameters, although even with these languages it is preferred to use output parameters as little as possible. 9/21/2018

73 Mechanics Create a temporary variable for the parameter.
Replace all references to the parameter, made after the assignment, to the temporary variable. Change the assignment to assign to the temporary variable. Compile and test. 9/21/2018

74 Example int discount (int inputVal, int quantity, int yearToDate) { if (inputVal > 50) inputVal -= 2; if (quantity > 100) inputVal -= 1; if (yearToDate > 10000) inputVal -= 4; return inputVal; } int result = inputVal; if (inputVal > 50) result -= 2; if (quantity > 100) result -= 1; if (yearToDate > 10000) result -= 4; return result; 9/21/2018

75 int discount (int inputVal, int quantity, int yearToDate) { int result = inputVal; if (inputVal > 50) result -= 2; if (quantity > 100) result -= 1; if (yearToDate > 10000) result -= 4; return result; } int discount (final int inputVal, final int quantity, final int yearToDate) { 9/21/2018

76 Java’s “passed by value”
class Param { public static void main(String[] args) { int x = 5; triple(x); System.out.println ("x after triple: " + x); } private static void triple(int arg) { arg = arg * 3; System.out.println ("arg in triple: " + arg); } arg in triple: 15 x after triple: 5 9/21/2018

77 class Param { public static void main(String[] args) { Date d1 = new Date ("1 Apr 98"); nextDateUpdate(d1); System.out.println ("d1 after nextDay: " + d1); Date d2 = new Date ("1 Apr 98"); nextDateReplace(d2); System.out.println ("d2 after nextDay: " + d2); } private static void nextDateUpdate (Date arg) { arg.setDate(arg.getDate() + 1); System.out.println ("arg in nextDay: " + arg); private static void nextDateReplace (Date arg) { arg = new Date (arg.getYear(), arg.getMonth(), arg.getDate() + 1); } arg in nextDay: Thu Apr 02 00:00:00 EST 1998 d1 after nextDay: Thu Apr 02 00:00:00 EST 1998 d2 after nextDay: Wed Apr 01 00:00:00 EST 1998 9/21/2018

78 Essentially the object reference is passed by value.
This allows us to modify the object but does not take into account the reassigning of the parameter. Java 1.1 and later versions allow us to mark a parameter as final; this prevents assignment to the variable but still allows you to modify the object the variable refers to. 9/21/2018

79 Replace Method with Method Object
Problem: You have a long method that uses local variables in such a way that you cannot apply Extract Method. Solution: 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. 9/21/2018

80 //class Order... double price() { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // long computation; ... } 9/21/2018

81 Motivation Emphasize the beauty of small methods.
By extracting pieces out of a large method, you make things much more comprehensible. The difficulty in decomposing a method lies in local variables. If they are rampant, decomposition can be difficult. Using Replace Temp with Query helps to reduce this burden, but occasionally you may find you cannot break down a method that needs breaking. Applying Replace Method with Method Object turns all these local variables into fields on the method object. You can then use Extract Method on this new object to create additional methods that break down the original method. 9/21/2018

82 Mechanics Create a new class, name it after the method.
Give the new class a final field for the object that hosted the original method (the source object) and a field for each temporary variable and each parameter in the method. Give the new class a constructor that takes the source object and each parameter. Give the new class a method named "compute." Copy the body of the original method into compute. Use the source object field for any invocations of methods on the original object. Compile. Replace the old method with one that creates the new object and calls compute. Now comes the fun part. Because all the local variables are now fields, you can freely decompose the method without having to pass any parameters. 9/21/2018

83 Example //Class Account int gamma (int inputVal, int quantity, int yearToDate) { int importantValue1 = (inputVal * quantity) + delta(); int importantValue2 = (inputVal * yearToDate) + 100; if ((yearToDate - importantValue1) > 100) importantValue2 -= 20; int importantValue3 = importantValue2 * 7; // and so on... return importantValue3 - 2 * importantValue1; } 9/21/2018

84 To turn this into a method object, declaring a new class.
A final field for the original object and a field for each parameter and temporary variable in the method. //class Gamma... private final Account _account; private int inputVal; private int quantity; private int yearToDate; private int importantValue1; private int importantValue2; private int importantValue3; 9/21/2018

85 inputVal = inputValArg; quantity = quantityArg;
Usually use the underscore prefix convention for marking fields. But to keep small steps, we leave the names as they are for the moment. Add a constructor: Gamma (Account source, int inputValArg, int quantityArg, int yearToDateArg) { _account = source; inputVal = inputValArg; quantity = quantityArg; yearToDate = yearToDateArg; } 9/21/2018

86 Now move the original method over.
Modify any calls of features of account to use the _account field int compute () { importantValue1 = (inputVal * quantity) + _account.delta(); importantValue2 = (inputVal * yearToDate) + 100; if ((yearToDate - importantValue1) > 100) importantValue2 -= 20; int importantValue3 = importantValue2 * 7; // and so on... return importantValue3 - 2 * importantValue1; } 9/21/2018

87 Modify the old method to delegate to the method object:
int gamma (int inputVal, int quantity, int yearToDate) { return new Gamma(this, inputVal, quantity, yearToDate).compute(); } 9/21/2018

88 importantValue1 = (inputVal * quantity) + _account.delta();
That's the essential refactoring. The benefit is that I can now easily use Extract Method on the compute method without ever worrying about the argument's passing: int compute () { importantValue1 = (inputVal * quantity) + _account.delta(); importantValue2 = (inputVal * yearToDate) + 100; importantThing(); int importantValue3 = importantValue2 * 7; // and so on... return importantValue3 - 2 * importantValue1; } void importantThing() { if ((yearToDate - importantValue1) > 100) importantValue2 -= 20; 9/21/2018

89 Substitute Algorithm Problem:
You want to replace an algorithm with one that is clearer. Solution: Replace the body of the method with the new algorithm. 9/21/2018

90 Motivation Refactoring can break down something complex into simpler pieces, but sometimes you just reach the point at which you have to remove the whole algorithm and replace it with something simpler. This occurs as you learn more about the problem and realize that there's an easier way to do it. It also happens if you start using a library that supplies features that duplicate your code. 9/21/2018

91 Mechanics Prepare your alternative algorithm. Get it so that it compiles. Run the new algorithm against your tests. If the results are the same, you're finished. If the results aren't the same, use the old algorithm for comparison in testing and debugging . Run each test case with old and new algorithms and watch both results. That will help you see which test cases are causing trouble, and how. 9/21/2018

92 String foundPerson(String[] people){
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 ""; 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 ""; } 9/21/2018

93 Moving Features Between Objects One of the most fundamental, if not the fundamental, decision in object design is deciding where to put responsibilities. So, this set of refactoring is all about object's responsibilities. 9/21/2018

94 Move Method Problem: You have one class doing work that should be done by two. Solution: Create a new class and move the relevant fields and methods from the old class into the new class. 9/21/2018

95 9/21/2018

96 Motivation When classes have too much behavior or when classes are collaborating too much and are too highly coupled. By moving methods around, the classes become simpler and they end up being a more crisp implementation of a set of responsibilities. Look through the methods on a class to find a method that seems to reference another object more than the object it lives on. A good time to do this is after some fields have been reviewed. Once a likely method is identified, look at the methods that call it, the methods it calls, and any redefining methods in the hierarchy to assess whether to go ahead on the basis of the object with which the method seems to have more interaction. It's not always an easy decision to make. If not sure, move on to look at other methods. If it is difficult to make the decision, it probably does not matter that much. 9/21/2018

97 Mechanics Examine all features used by the source method that are defined on the source class. Consider whether they also should be moved. If a feature is used only by the method you are about to move, you might as well move it, too. If the feature is used by other methods, consider moving them as well. Sometimes it is easier to move a clutch of methods than to move them one at a time. Check the sub- and superclasses of the source class for other declarations of the method. If there are any other declarations, you may not be able to make the move, unless the polymorphism can also be expressed on the target. Declare the method in the target class. You may choose to use a different name, one that makes more sense in the target class. Copy the code from the source method to the target. Adjust the method to make it work in its new home. If the method uses its source, you need to determine how to reference the source object from the target method. If there is no mechanism in the target class, pass the source object reference to the new method as a parameter. If the method includes exception handlers, decide which class should logically handle the exception. If the source class should be responsible, leave the handlers behind. Compile the target class. 9/21/2018

98 Mechanics Determine how to reference the correct target object from the source. There may be an existing field or method that will give you the target. If not, see whether you can easily create a method that will do so. Failing that, you need to create a new field in the source that can store the target. This may be a permanent change, but you can also make it temporarily until you have refactored enough to remove it. Turn the source method into a delegating method. Compile and test. Decide whether to remove the source method or retain it as a delegating method. Leaving the source as a delegating method is easier if you have many references. If you remove the source method, replace all the references with references to the target method. You can compile and test after changing each reference, although it is usually easier to change all references with one search and replace. 9/21/2018

99 Example class Account... double overdraftCharge() { if (_type.isPremium()) { double result = 10; if (_daysOverdrawn > 7) result += (_daysOverdrawn - 7) * 0.85; return result; } else return _daysOverdrawn * 1.75; double bankCharge() { double result = 4.5; if (_daysOverdrawn > 0) result += overdraftCharge(); private AccountType _type; private int _daysOverdrawn; 9/21/2018

100 class AccountType... double overdraftCharge(int daysOverdrawn) {
if (isPremium()) { double result = 10; if (daysOverdrawn > 7) result += (daysOverdrawn - 7) * 0.85; return result; } else return daysOverdrawn * 1.75; 9/21/2018

101 class Account. double overdraftCharge() { return _type
class Account... double overdraftCharge() { return _type.overdraftCharge(_daysOverdrawn); } 9/21/2018

102 class Account... double bankCharge() { double result = 4.5;
if (_daysOverdrawn > 0) result += _type.overdraftCharge(_daysOverdrawn); return result; } 9/21/2018

103 class AccountType... double overdraftCharge(Account account) { if (isPremium()) { double result = 10; if (account.getDaysOverdrawn() > 7) result += (account.getDaysOverdrawn() - 7) * 0.85; return result; } else return account.getDaysOverdrawn() * 1.75; 9/21/2018

104 Move Field Problem: A field is, or will be, used by another class more than the class on which it is defined. Solution: Create a new field in the target class, and change all its users. 9/21/2018

105 9/21/2018

106 Motivation Moving state and behavior between classes is the very essence of refactoring. As the system develops, the need for new classes and the need to shuffle responsibilities around arises. A design decision that is reasonable and correct one week can become incorrect in another. Moving a field if more methods on another class using the field than the class itself. This usage may be indirect, through getting and setting methods. One may choose to move the methods; But if the methods seem sensible where they are, one can move the field. Another reason for field moving is when doing Extract Class. In that case the fields go first and then the methods. 9/21/2018

107 Mechanics If the field is public, use Encapsulate Field.
If you are likely to be moving the methods that access it frequently or if a lot of methods access the field, you may find it useful to use Self Encapsulate Field Compile and test. Create a field in the target class with getting and setting methods. Compile the target class. Determine how to reference the target object from the source. An existing field or method may give you the target. If not, see whether you can easily create a method that will do so. Failing that, you need to create a new field in the source that can store the target. This may be a permanent change, but you can also do it temporarily until you have refactored enough to remove it. Remove the field on the source class. Replace all references to the source field with references to the appropriate method on the target. For accesses to the variable, replace the reference with a call to the target object's getting method; for assignments, replace the reference with a call to the setting method. If the field is not private, look in all the subclasses of the source for references. 9/21/2018

108 Example class Account... private AccountType _type; private double _interestRate; double interestForAmount_days (double amount, int days) { return _interestRate * amount * days / 365; } 9/21/2018

109 class AccountType... private double _interestRate; void setInterestRate (double arg) { _interestRate = arg; } double getInterestRate () { return _interestRate; 9/21/2018

110 private double _interestRate; double interestForAmount_days (double amount, int days) { return _type.getInterestRate() * amount * days / 365; } 9/21/2018

111 double interestForAmountAndDays (double amount, int days) { return getInterestRate() * amount * days / 365; } private void setInterestRate (double arg) { _type.setInterestRate(arg); private double getInterestRate () { return _type.getInterestRate(); 9/21/2018

112 Example: Using Self-Encapsulation
class Account... private AccountType _type; private double _interestRate; double interestForAmount_days (double amount, int days) { return getInterestRate() * amount * days / 365; } private void setInterestRate (double arg) { _interestRate = arg; private double getInterestRate () { return _interestRate; 9/21/2018

113 Extract class Problem:
You have one class doing work that should be done by two. Solution: Create a new class and move the relevant fields and methods from the old class into the new class. 9/21/2018

114 You've probably heard that a class should be a crisp abstraction, handle a few clear responsibilities, or some similar guideline. In practice, classes grow. You add some operations here, a bit of data there. You add a responsibility to a class feeling that it's not worth a separate class, but as that responsibility grows and breeds, the class becomes too complicated. Soon your class is as crisp as a microwaved duck. You've probably heard that a class should be a crisp abstraction, handle a few clear responsibilities, or some similar guideline. In practice, classes grow. You add some operations here, a bit of data there. You add a responsibility to a class feeling that it's not worth a separate class, but as that responsibility grows and breeds, the class becomes too complicated. Soon your class is as crisp as a microwaved duck. 9/21/2018

115 Motivation A class should be a crisp abstraction, handle a few clear responsibilities, or some similar guideline. In practice, classes and responsibilities of classes grows and breeds, the class becomes too complicated. Soon your class is as crisp as a microwaved duck. A class with many methods and quite a lot of data, too big to understand easily. 9/21/2018

116 Motivation Where it can be split?
A good sign is that a subset of the data and a subset of the methods seem to go together. Other good signs are subsets of data that usually change together or are particularly dependent on each other. One sign that often crops up later in development is the way the class is subtyped. You may find that subtyping affects only a few features or that some features need to be subtyped one way and other A useful test is to ask yourself what would happen if you removed a piece of data or a method. What other fields and methods would become nonsense? 9/21/2018

117 Mechanics Decide how to split the responsibilities of the class.
Create a new class to express the split-off responsibilities. If the responsibilities of the old class no longer match its name, rename the old class. Make a link from the old to the new class. You may need a two-way link. But don't make the back link until you find you need it. Use Move Field on each field you wish to move. Compile and test after each move. Use Move Method to move methods over from old to new. Start with lower-level methods (called rather than calling) and build to the higher level. Review and reduce the interfaces of each class. If you did have a two-way link, examine to see whether it can be made one way. Decide whether to expose the new class. If you do expose the class, decide whether to expose it as a reference object or as an immutable value object. 9/21/2018

118 Example public String getName() { return _name; }
class Person... public String getName() { return _name; } public String getTelephoneNumber() { return ("(" + _officeAreaCode + ") " + _officeNumber); String getOfficeAreaCode() { return _officeAreaCode; void setOfficeAreaCode(String arg) { _officeAreaCode = arg; String getOfficeNumber() { return _officeNumber; void setOfficeNumber(String arg) { _officeNumber = arg; private String _name; private String _officeAreaCode; private String _officeNumber; 9/21/2018

119 class TelephoneNumber { } class Person private TelephoneNumber _officeTelephone = new TelephoneNumber(); 9/21/2018

120 class Person private TelephoneNumber _officeTelephone = new TelephoneNumber();
9/21/2018

121 class TelephoneNumber { String getAreaCode() { return _areaCode; } void setAreaCode(String arg) { _areaCode = arg; private String _areaCode; class Person... public String getTelephoneNumber() { return ("(" + getOfficeAreaCode() + ") " + _officeNumber); String getOfficeAreaCode() { return _officeTelephone.getAreaCode(); void setOfficeAreaCode(String arg) { _officeTelephone.setAreaCode(arg); 9/21/2018

122 class Person... public String getName() { return _name; } public String getTelephoneNumber(){ return _officeTelephone.getTelephoneNumber(); TelephoneNumber getOfficeTelephone() { return _officeTelephone; private String _name; private TelephoneNumber _officeTelephone = new TelephoneNumber(); class TelephoneNumber... public String getTelephoneNumber() { return ("(" + _areaCode + ") " + _number); String getAreaCode() { return _areaCode; void setAreaCode(String arg) { _areaCode = arg; String getNumber() { return _number; void setNumber(String arg) { _number = arg; private String _number; private String _areaCode; 9/21/2018

123 Inline class Problem: A class isn't doing very much. Solution:
Move all its features into another class and delete it. 9/21/2018

124 Motivation Inline Class is the reverse of Extract Class.
It is used if a class is no longer pulling its weight and shouldn't be around any more. Often this is the result of refactoring that moves other responsibilities out of the class so there is little left. Then it is a good idea to fold this class into another class, picking one that seems to use the runt class the most. 9/21/2018

125 Mechanics Declare the public protocol of the source class onto the absorbing class. Delegate all these methods to the source class. If a separate interface makes sense for the source class methods, use Extract Interface before inlining. Change all references from the source class to the absorbing class. Declare the source class private to remove out-of-package references. Also change the name of the source class so the compiler catches any dangling references to the source class. Compile and test. Use Move Method and Move Field to move features from the source class to the absorbing class until there is nothing left. Hold a short, simple funeral service. 9/21/2018

126 class Person... public String getName() { return _name; } public String getTelephoneNumber(){ return _officeTelephone.getTelephoneNumber(); TelephoneNumber getOfficeTelephone() { return _officeTelephone; private String _name; private TelephoneNumber _officeTelephone = new TelephoneNumber(); class TelephoneNumber... public String getTelephoneNumber() { return ("(" + _areaCode + ") " + _number); String getAreaCode() { return _areaCode; void setAreaCode(String arg) { _areaCode = arg; String getNumber() { return _number; void setNumber(String arg) { _number = arg; private String _number; private String _areaCode; Example 9/21/2018

127 class Person. String getAreaCode() { return _officeTelephone
class Person... String getAreaCode() { return _officeTelephone.getAreaCode(); } void setAreaCode(String arg) { _officeTelephone.setAreaCode(arg); String getNumber() { return _officeTelephone.getNumber(); void setNumber(String arg) { _officeTelephone.setNumber(arg); 9/21/2018

128 Person martin = new Person(); martin. getOfficeTelephone()
Person martin = new Person(); martin.getOfficeTelephone().setAreaCode ("781"); Become martin.setAreaCode ("781"); Continue to use Move field and Move Method 9/21/2018

129 Hide Delegate Problem:
A client is calling a delegate class of an object. Solution: Create methods on the server to hide the delegate. 9/21/2018

130 Motivation One of the keys, if not the key, to objects is encapsulation. Encapsulation means that objects need to know less about other parts of the system. Then when things change, fewer objects need to be told about the change—which makes the change easier to make. Anyone involved in objects knows that you should hide your fields, despite the fact that Java allows fields to be public. As you become more sophisticated, you realize there is more you can encapsulate. If a client calls a method defined on one of the fields of the server object, the client needs to know about this delegate object. If the delegate changes, the client also may have to change. You can remove this dependency by placing a simple delegating method on the server, which hides the delegate. Changes become limited to the server and don't propagate to the client. 9/21/2018

131 9/21/2018

132 Mechanics For each method on the delegate, create a simple delegating method on the server. Adjust the client to call the server. If the client is not in the same package as the server, consider changing the delegate method's access to package visibility. Compile and test after adjusting each method. If no client needs to access the delegate anymore, remove the server's accessor for the delegate. Compile and test. 9/21/2018

133 Example class Person { Department _department; public Department getDepartment() { return _department; } public void setDepartment(Department arg) { _department = arg; class Department { private String _chargeCode; private Person _manager; public Department (Person manager) { _manager = manager; public Person getManager() { return _manager; ... 9/21/2018

134 manager = john.getDepartment().getManager();
9/21/2018

135 public Person getManager() { return _department.getManager(); }
9/21/2018

136 manager = john.getManager();
9/21/2018

137 Remove Middle Man Problem:
A class is doing too much simple delegation. Solution: Get the client to call the delegate directly. 9/21/2018

138 Motivation In the motivation for Hide Delegate, the advantages of encapsulating the use of a delegated object. There is a price for this. The price is that every time the client wants to use a new feature of the delegate, you have to add a simple delegating method to the server. After adding features for a while, it becomes painful. The server class is just a middle man, and perhaps it's time for the client to call the delegate directly. It's hard to figure out what the right amount of hiding is. Fortunately, with Hide Delegate and Remove Middle Man it does not matter so much. You can adjust your system as time goes on. As the system changes, the basis for how much you hide also changes. A good encapsulation six months ago may be awkward now. Refactoring means you never have to say you're sorry—you just fix it. 9/21/2018

139 Mechanics Create an accessor for the delegate.
For each client use of a delegate method, remove the method from the server and replace the call in the client to call method on the delegate. Compile and test after each method. 9/21/2018

140 Example class Person... Department _department; public Person getManager() { return _department.getManager(); class Department... private Person _manager; public Department (Person manager) { _manager = manager; } 9/21/2018

141 manager = john.getManager();
9/21/2018

142 class Person... public Department getDepartment() { return _department; }
9/21/2018

143 manager = john.getDepartment().getManager();
9/21/2018

144 A server class you are using needs several
Problem: A server class you are using needs several additional methods, but you can't modify the class. Solution 1 - Introduce foreign method: Create a method in the client class with an instance of the server class as its first argument. Solution 2 – Introduce local extension: Create a new class that contains these extra methods. Make this extension class a subclass or a wrapper of the original. 9/21/2018

145 Foreign Method Problem: A server class you are using needs several
additional methods, but you can't modify the class. Solution: Create a method in the client class with an instance of the server class as its first argument. 9/21/2018

146 Motivation You are using this really nice class that gives you all these great services. Then there is one service it doesn't give you but should. You curse the class, saying, "Why don't you do that?" 9/21/2018

147 Mechanics Create a method in the client class that does what you need.
The method should not access any of the features of the client class. If it needs a value, send it in as a parameter. Make an instance of the server class the first parameter. Comment the method as "foreign method; should be in server." This way you can use a text search to find foreign methods later if you get the chance to move the method. 9/21/2018

148 Example Date newStart = new Date (previousEnd.getYear(),
previousEnd.getMonth(), previousEnd.getDate() + 1); Date newStart = nextDay(previousEnd); private static Date nextDay(Date arg) { return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1); } 9/21/2018

149 Introduce local extension
Problem: A server class you are using needs several additional methods, but you can't modify the class. Solution: Create a new class that contains these extra methods. Make this extension class a subclass or a wrapper of the original. 9/21/2018

150 9/21/2018

151 Extract subclass Problem:
A class has features that are only used by some instances. Solution: Create a new subclass. Iteratively push down methods and features. 9/21/2018

152 9/21/2018

153 Extract Subclass 9/21/2018

154 Extract superclass Problem:
Two classes have similar features. This is a form of code duplication. Solution: Introduce an abstract superclass. Iteratively use Pull Up Field, Pull Up Method, and Pull Up Constructor Body. 9/21/2018

155 9/21/2018

156 9/21/2018

157 Pull up field Problem: Two subclasses have the same field. Solution:
Move the field to the superclass. 9/21/2018

158 Pull Up Constructor Body
Problem: You have constructors on subclasses with mostly identical bodies. Solution: Create a superclass constructor to do the work; call this from the subclass methods. 9/21/2018

159 Push Down Method Problem:
Behavior on a superclass is relevant only for some of its subclasses. Solution: Move it to those subclasses. Quota:配额 9/21/2018

160 Replace inheritance with delegation
Problem: A subclass uses only part of a superclasses interface or does not want to inherit data. Solution: Create a field for the superclass, adjust methods to delegate to the superclass, and remove the subclassing. 9/21/2018

161 9/21/2018

162 Replace delegation with inheritance
Problem: You're using delegation and are often writing many simple delegations for the entire interface. Solution: Make the delegating class a subclass of the delegate. 9/21/2018

163 9/21/2018

164 References http://www.cs.sjsu.edu/faculty/pearce/tdd/intro.htm
9/21/2018

165 Pull Up Method 9/21/2018

166 Push Down Field 9/21/2018

167 Extract Interface 9/21/2018

168 Collapse Hierarchy 9/21/2018

169 Form Template Method 9/21/2018

170 9/21/2018


Download ppt "Introduction to Refactoring"

Similar presentations


Ads by Google