Presentation is loading. Please wait.

Presentation is loading. Please wait.

How and When to do Refactoring CSE301 University of Sunderland Harry R. Erwin, PhD.

Similar presentations


Presentation on theme: "How and When to do Refactoring CSE301 University of Sunderland Harry R. Erwin, PhD."— Presentation transcript:

1 How and When to do Refactoring CSE301 University of Sunderland Harry R. Erwin, PhD

2 Resources Eclipse automates a lot of refactoring (8)). Fowler, 2000, Refactoring: Improving the Design of Existing Code, Addison-Wesley. Note: this is available electronically at the library. http://www.amazon.com/exec/obidos/tg/detail/- /0321109295/103-4060125-0311065http://www.amazon.com/exec/obidos/tg/detail/- /0321109295/103-4060125-0311065 http://www.amazon.com/exec/obidos/ASIN/01306 48841/103-4060125-0311065http://www.amazon.com/exec/obidos/ASIN/01306 48841/103-4060125-0311065 http://www.refactoring.com/catalog/ http://www.win.ua.ac.be/~lore/refactoringProject/i ndex.phphttp://www.win.ua.ac.be/~lore/refactoringProject/i ndex.php

3 Final Year Project Comments A few comments on OO programming. Important refactorings will be in dark blue. –Don't translate a C program into Java. Think objects. –Avoid overusing static fields and methods. That suggests you're writing a C program in Java. Don't go there. –Use 'move method' to push responsibilities into subclasses. Students often define methods in the calling classes rather than in the called classes, and then need to correct the design. –Look for polymorphism. It can be used rather elegantly. –Are you using JUnit and TDD? Use the existing suite and read the documentation about asserts!

4 Refactoring that Eclipse Supports Already! Rename Move Change Method Signature Convert Anonymous Class to Nested Class Change Nested Class to Top-Level Class Push Down Pull Up Extract Interface Use Supertype Where Possible Inline Extract Method Extract Local Variable Extract Constant Convert Local Variable to Field Encapsulate Field

5 A Catalog of Refactorings Composing Methods Moving Features Between Objects Organizing Data Simplifying Conditionals Making Method Calls Simpler Generalization Big Refactorings

6 The Basic Rule of Refactoring “Refactor the low hanging fruit” http://c2.com/cgi/wiki?RefactorLowHangingFruit http://c2.com/cgi/wiki?RefactorLowHangingFruit Low Hanging Fruit (def): “The thing that gets you most value for the least investment.” In other words, don’t spend much time on it. There are ways to improve any design incrementally. We will explore a few of them.

7 The Goal of Refactoring To improve code without changing what it does. This in some ways is similar to how an optimizing compiler restructures code. These how-to’s describe things you can do. Think about why they work!

8 Composing Methods Extract Method Inline Method Replace Method with Method Object Substitute Algorithm

9 Extract Method Extract Method is one of the most important refactorings. If you see a patch of coherent code that does something like compose a pane in a GUI encapsulate it as a method. This is my favorite refactoring; usually I apply it when a method gets too long. Take a clump of related code and make it into a small method. eclipse handles this well. It does have problems with local variables….

10 Example for Extract Method final JCheckBox oos = new JCheckBox( "Out of Supply ",unit.outOfSupply); ActionListener oosListener = new ActionListener(){ public void actionPerformed(ActionEvent e){ theUnit.outOfSupply = oos.isSelected(); theBattle.recompute(); } }; oos.addActionListener(oosListener); res.add(oos);

11 That Code Becomes final JCheckBox oos = addOOS(unit, theUnit); res.add(oos); AND private JCheckBox addOOS(Ground unit, final Ground theUnit) { final JCheckBox oos = new JCheckBox( "Out of Supply ",unit.outOfSupply); ActionListener oosListener = new ActionListener(){ public void actionPerformed(ActionEvent e){ theUnit.outOfSupply = oos.isSelected(); theBattle.recompute(); } }; oos.addActionListener(oosListener); return oos; }

12 Inline Method Reverses Extract MethodExtract Method You use Inline Method when the code the results from Extract Method is uglier than the original. You also use it when the method’s body is just as clear as its name. Or if you have a group of badly factored methods—inline them and then re-extract methods. Remember to get rid of the method—needless indirection is irritating and makes you look dumb.

13 Replace Method with Method Object Replace Method with Method Object moves local variables into class fields of a ‘functor’, so Extract Method can be used more easily.Extract Method The curse of extracting methods is local variables. So introduce method objects if you have a big method with lots of local variables. Applying this refactoring converts all those local variables to fields of the method object. Then you can extract methods freely.

14 Method Object Implementation (after Beck via Fowler) Create a new class named after the method. Define a final field for the object that owned the original method and a modifiable field for every local variable. Define a constructor with those fields as arguments. Give the class a method named ‘compute()’. Replace the old method with one that creates the object and calls compute(). int gamma(int val1, int val2, int val3) { return new Gamma(this,val1,val2,val3).compute(); } Now you can freely decompose compute() without passing any parameters. This is a good thing.

15 Substitute Algorithm This one sounds a bit dumb. Have you thought of a smarter or clearer way of doing something? Go ahead and replace the method with the new algorithm. If you’re using test-driven development, you can quickly check whether the new version works. If it doesn’t, backtrack, and nobody will ever know…

16 Moving Features Between Objects A fundamental design decision is where to put responsibilities. You’ll never get it right the first time. Refactoring allows you to change your mind. When you want to move functions between objects, these refactorings handle it. Move Method, Move Field, and Extract/Inline Class are the heavyweights here.

17 Move Field “Moving state and behavior between classes is the very essence of refactoring.” (Fowler) You do this to shuffle responsibilities around. Consider a move if a field is used more by other classes than by its home class. If you do an Extract Class, the fields get moved first and then the methods follow them.Extract Class You may need to encapsulate a field first. Then move the field and point any accessors at the new location, perhaps using new accessors there.encapsulate a field

18 Move Method “The bread and butter of refactoring.” (Fowler) A method is used by another class more than by its home class. I know of cats like that. Create a similar method in the other class. Either convert the old method to a simple delegation or remove it entirely. Think about doing this after you’ve moved some fields between classes. Warning: watch for polymorphism on the original class. That can make the move impossible.

19 Extract Class Classes grow, and you may have one class doing work that should be done by two. You find you’re violating the Single Responsibility Principle (SRP) with lots of data and methods. Time to get the scalpel out. Create a new class and move the relevant fields and methods there. Delegate! If you don’t know why the SRP is important, wait till next semester.

20 Inline Class You find a class isn’t doing very much. This often occurs after a refactoring that has removed fields and methods. Move its features into another class and then delete it. Use Encapsulate Field, Move Field, and Move Method to do this.Encapsulate FieldMove Field Move Method

21 Hide Delegate A client calls a method defined on a field of a server object, possibly violating encapsulation. theServer.getManager().getDepartment(); To do this, the client needs to know that theServer has a Manager field. Replace with a getDepartment() method for theServer. Then unless getManager() is needed, remove it. theServer.getDepartment();

22 Remove Middle Man “Here, you take the phone.” After some development, you notice that a class is doing a lot of simple delegation. Get the client to call the delegate directly. You may need to create an accessor for the delegate, but thereafter a method only needs to call on the delegate.

23 End of First Hour

24 Organizing Data These refactorings generally clean up problems with how classes define and access fields. The most interesting refactoring here is probably Duplicate Observed Data. That’s how you fix a class that mixes business logic with GUI or SQL code. We’ll spend some time on it since Fowler isn’t very clear. Duplicate Observed Data By the way, value objects are objects that are equal if their fields are equal—you override equals() and hashCode() for them.

25 Self Encapsulate Field You are accessing a field of a class directly in a method, but use of the field is becoming awkward. Create getting and setting methods (‘accessors’) for the field and make the field private. Use these methods within the class. Why not always do this? Because getting and setting methods obscure what the code is doing. Doing this is particularly important if a subclass may later need to override direct access with a computed value. A closely related refactoring: Encapsulate Field.Encapsulate Field.

26 Replace Data Value with Object You find that you have a data item (a primitive or value type) that needs additional data or behavior. For example, the data might be a String that has a special format. Turn the data item into an object that holds the original data as a final field. Provide a constructor and a getting method. If you need a setting method, have it create a new instance of the class. You may need to Change Value to Reference, if multiple instances need to share the data object.Change Value to Reference

27 Reference objects stand for one object in the real world. Value objects are defined entirely through their data values. You need to know when two are equal, so you have to override equals() and hashCode(). A class has many instances, many of which have the same value. You want to replace them with a single instance. –Replace the value constructor with a factory method. –Decide how to provide access to the objects. –Decide whether to precreate or create as required. –Alter the factory method to return the reference object.

28 Change Reference to Value You have a reference type object that is small, immutable, and awkward to manage. Turn it into a value object. –Check that it is really immutable (final) or can become immutable. –Create an equals() and a hashCode() method. The easiest way to write hashCode() is to do a bitwise xor (^) on all the hashcodes of the fields referred to in the equals() method. Primitive types need to be ‘boxed’ for this. E.g., to box i, Integer iBoxed = new Integer(i); –The consider removing the factory method and replacing it with a public constructor. –Finally make the contents final.

29 Replace Array with Object You have inherited a design that uses an array where different elements mean different things. This is bad programming practice. Replace with an object that has a field for each element. You do this by creating getters and setters for each entry in the array, creating the fields, and then removing the array and repointing the getters and setters at the new fields.

30 Duplicate Observed Data This is how you pull a domain model (the business logic) out of a poorly-designed system with no model. If there are multiple windows, see Separate Domain from Presentation. Separate Domain from Presentation. Copy the business data in the system to a domain object. Set up observers to synchronize the domain model with whatever is dependent on it. This is a tricky dribble, so pay close attention.

31 DOD Implementation (Part 1) Start with the dependent objects (e.g., parts of the GUI, other interfaces or a database). Create a domain class as an Observable. Create references to the dependent objects in the domain class. Make the dependent objects Observers of the domain class, so that if it changes, they change. Write an update() method for each. Each constructor has to connect to the domain class, add itself as an Observer of that class, and finally call its update() method on the domain class instance. Use Self-Encapsulate Data on any domain data in the dependent objects. Test—behavior should not change.Self-Encapsulate Data

32 DOD Implementation (Part 2) Now for GUIs, add a method to the event handler that uses the new setting methods to update the component with its current value using direct access to the field. This ensures user input goes through the setting method. This will be important later. Test. Define corresponding data fields and getting/setting methods in the domain class. Setting methods need to trigger model updates and the notify mechanism. Test. Redirect the accessors for the dependent classes to read and write the domain class fields. Modify the observer’s update method to copy from the domain fields to the dependent class using direct access. Test one last time.

33 Change Unidirectional to Bidirectional Association You need a backpointer? First, lie down to see if the need passes. If it doesn’t, do the following: –Add a field for the backpointer. –Decide which class controls the link. –Create a helper method on the other side to send back the backpointer. –If the existing link modifier is on the controlling side, just modify it to update the backpointers. –If not, have it call a routine on the other side to update the backpointers.

34 Change Bidirectional to Unidirectional Association Reduce bidirectional to unidirectional whenever possible. If nobody needs one direction, simply remove it. For clients who do need the other direction: –Try Encapsulate Field on the backpointerEncapsulate Field –Then try Substitute Algorithm on the getter and testSubstitute Algorithm –If clients don’t need the getter, change each to get the object in the field some other way. –When no reader is left, dump the updater and remove the backpointer.

35 Encapsulate Field A class field is publically accessible. Hide it. Some people consider this good programming practice. Other people find it makes code inefficient or hard to read. YMMV. Provide accessors—getField(), setField(arg) Then find all users and change them to use the accessors. Finally, make the field private. This will tell you if you have really found all the users. Closely related to Self Encapsulate Field.Self Encapsulate Field

36 Encapsulate Collection If a class contains a collection of instances, getters and setters don’t work really well. First, initialize an empty collection when you create it. Encapsulate the collection field to create a getter and setter.Encapsulate the collection field Next provide public methods to add and remove elements. Modify users to use them instead. Modify your getter to return a read-only iterator (see the Collections class for how). Think about moving code that uses the getter into the class that owns the collection.

37 Replace Type Code With Class –When a class has a numeric type code that doesn’t affect its behavior—replace with a new class for each code. With Subclasses –If the type code does affect behavior, use subclasses With State/Strategy –Or replace the type code with a state object if subclassing doesn’t work

38 Replace Subclass with Fields You have subclasses that differ only in methods that return constant (final) data. Define final fields to contain the data, eliminate the subclasses, and return the field values instead.

39 Simplifying Conditionals These simplify conditional logic (if/then/else). Decompose Conditional is the most important. The elements of an if/then/else are replaced with method calls. These refactorings can also clean up if/then/else messes where flags are used to control sequencing or returns. Decompose Conditional Switch statements should be replaced with polymorphism. A null object is a real ‘do-nothing’ object that substitutes for a null value in a reference.

40 Decompose Conditional Suppose you have a complicated if-then- else (or ?:) statement. Use Extract Method on the condition, the then part, and the else part.Extract Method If there is a nested conditional, try Replace Nested Conditional with Guard Clauses first.Replace Nested Conditional with Guard Clauses

41 Consolidate Conditional Expression If you have a long list of ‘if(cond i ) return 0;’ statements in a method, combine them into a single conditional and extract it. This does the same thing, is clearer, and sets you up to use Extract Method.Extract Method.

42 Consolidate Duplicate Conditional Fragments The same fragment of code is in all branches of a conditional expression. Then move it outside of the expression. This makes things clearer. Consider using Extract Method, too. Extract Method,

43 Replace Nested Conditional with Guard Clauses If a method contains a complicated if-then- else expression that doesn’t make clear what the normal path is, use ‘guard clauses’ for all the special cases. Handle each special case with an immediate return. Then execute the normal path. Be clear!

44 Replace Conditional with Polymorphism Indicator—you have logic that chooses different behavior based on the type of an object or the value of a flag. Consider splitting the behavior among subclasses. Make the original method abstract. You may have to replace type codes with subclasses or state/strategy first.

45 Introduce Null Object You’re getting very tired of checking for null. This is often the case for framework code. Then replace the null value with a do-nothing ‘null object’. (This is what an Adaptor class is in swing, by the way.) This has the advantage of inheriting from Object (or even from the base class of your hierarchy). Unlike null, a ‘null object’ can have do-nothing methods called on it. A lot of if-then-else cases then disappear.

46 Making Method Calls Simpler These refactorings make interfaces more straightforward. Rename Method is convenient to document what a method does.Rename Method Most of the remaining refactorings are used to get rid of parameters, but be cautious in concurrent programming. Factory methods hide the concrete implementation of an interface.

47 Rename Method You have a method with a name like ‘foo’. Change the name to mean something. This is important when you have lots of small methods as a form of documentation. eclipse handles this very well.

48 Separate Query from Modifier You have a method that returns a value but also changes the state of an object. This violates the SRP (discussed later). Break the method into two: one returns the value, and the other changes the state.

49 Preserve Whole Object/ Replace Parameter with Method You get values from an object and use them as parameters to a method call. Why not simply pass the object to the method and let it get its own values? (You can even create a parameter object to do this.) If you call a method to get a value to pass as a parameter to a second method, why not let the second method get the parameter?

50 Replace Constructor with Factory Method You find you need to do more than simple construction when you create an object. Make the constructor private and replace it with a factory method. Factory methods are often static methods that do a smart construction. See the Singleton pattern for an example.

51 Generalization These refactorings clean up inheritance hierarchies. They also allow you to ‘evolve’ the hierarchy. Template methods are methods that call private methods that can be subclassed to replace multiple methods with the same signature. Sometimes delegation works better than inheritance or vice versa, so don’t be afraid to try it both ways.

52 Pull Up/Push Down Field/Method These refactorings move fields and methods within an inheritance hierarchy to improve the structure and eliminate duplication. You may need to rename related fields or methods to do this when they have acquired different names during development. Take the method or field code, copy it to the new location, and remove it from the old.

53 Pull Up Constructor Body You have constructors on subclasses that are mostly identical. You can’t use Pull Up Method because constructors aren’t inherited. 8) Create a superclass constructor containing the common code and call this from the subclass methods using super(args).

54 Extract Subclass/ Superclass/Interface When a class has features that are used only in certain cases, give those features to subclasses. This is similar to Extract Class using delegation. Extract Subclass is usually simpler but more limited than Extract Class.Extract Class Push down the methods first and later the fields. Watch for fields that you can throw away. You can also use this to define a ‘null object’. Extract Superclass is the reverse process. Extract Interface creates an interface the same way.

55 Collapse Hierarchy After a while, a class hierarchy grows complex. If you refactor it a bit, moving fields and methods around and extracting classes and interfaces, you may discover that some classes no longer do anything useful. Get rid of them. Choose a class to sacrifice, move its remaining fields and methods, and kill it. Don’t get blood on your clothes.

56 Form Template Method You have two methods associated with sibling subclasses that perform similar operations in the same order, but the operations are not the same. Exploit polymorphism. Extract the operations into methods with the same signature (names and argument lists), so that the original methods become the same. You may need to rename one. Then you can pull the original methods up into a superclass, and the differences live in the subclasses.

57 Replace Inheritance with Delegation (and vice versa) See the discussion of Extract Class versus Extract Subclass.Extract Class Extract Subclass A subclass only uses part of a superclass interface or doesn’t use inherited data. Change the subclass to a separate class and delegate. Or the reverse… Whatever works.

58 Big Refactorings Tease Apart Inheritance Convert Procedural Design to Objects Separate Domain from Presentation Extract Hierarchy

59 Tease Apart Inheritance Your design was by a C++ programmer who believed in multiple inheritance. 8( Create two hierarchies from the complex one and use delegation to invoke one from the other. –The more important of the two responsibilities should define inheritance in the original tree. –The lesser responsibility should have its own tree. –Link them at the hip (abstract base class to abstract base class) so that to the outside it looks like a single complex hierarchy. –You may need to Replace Constructor with Factory Method.Replace Constructor with Factory Method

60 Convert Procedural Design to Objects Someone who never took CSE301 did an OO design. It looks like a C program 8(. –Turn data into objects. Each record type should become a dumb data object with get and set operations. –Take all the procedural code and give it to a main class (creating a big heap of powerful and fertile material, aka BS). –Break up the main class behavior (using a shovel and Extract Method).Extract Method –Move the behavior into the objects (using a rake and Move Method).Move Method –Finally delete the original main class, flush it down the sewer, and celebrate with a beer.

61 Separate Domain from Presentation Someone who never took CSE301 designed a GUI program 8(. –Create a domain class for each window. –If the data on a window forms a grid, create classes for each row and a collection to hold the rows. –Separate the presentation from the domain using Move Field and Duplicate Observed Data.Move Field Duplicate Observed Data –Use Extract Method and Move Method to do the same for behavior.Extract Method Move Method –Refactor the resulting mess mercilessly to clean up.

62 Extract Hierarchy You have a class that has a zillion special cases since the designer never heard of the SRP or polymorphism 8(. Make a list of important special cases. Extend it as you learn more. Take your time to get it right. –Create a subclass for each special case. –Use Replace Constructor with Factory Method to produce the subclasses.Replace Constructor with Factory Method –Methods with conditional logic should Replace Conditional with Polymorphism or Extract Method.Replace Conditional with Polymorphism Extract Method –Refactor mercilessly to clean up.

63 Beck’s Comments on Putting it all Together Even if you know the refactorings, you may not know when to do it. Beck claims there is a rhythm to refactoring. Practice it. You’re getting it when you start to feel confident that you can take anything that has been screwed up and make it better. You’re getting it when you can ‘stop with confidence.’ You’ve done enough. If the code is better, release it. If not, flush it.

64 The Feeling You Want You have a different relationship with your program. The design is fluid and plastic and under your control. It changes and adapts to changes in your requirements. You can feel it.

65 Learning Refactoring Pick a goal—something’s wrong with the program. Resolve to fix it. Then fix it. Stop when you’re unsure. Backtrack. Use test-driven development to make sure you haven’t broken something. If you have, back up. Work with a friend. ‘We explain to each other what we don’t understand.’ You’ll learn a lot in the process.


Download ppt "How and When to do Refactoring CSE301 University of Sunderland Harry R. Erwin, PhD."

Similar presentations


Ads by Google