Gregor Gisler-Merz BrownBag Session Refactoring
Gregor Gisler-Merz Content: Refactoring – The big picture Definition of refactoring A sample General rules of thumb Refactoring categories according to Fowler Where do I find a catalog for common refactorings? What indicators are there for a refactoring? Bad smells (1-4) When is the right time for a refactoring? Bigger refactorings Architectural Smells: indicators for bigger refactorings! References
Gregor Gisler-Merz Refactoring – The big picture: Everybody can write machine interpretable code but only good programmers can write code understandable by human beings! Emergent Design - versus – BigDesignUpfront. Refactoring is the main tool with an Emergent Design approach. Refactorings can be induced by technological change (introduction of new technology) or due to new requirements. What is the difference between a refactoring and a redesign? –Refactoring is ongoing over the livecycle –Redesign and Reengineering tasks are big and costly and indicate a lack of refactoring during the product livecycle Refactorings are necessary especially in agile environments where the release cycles are short and the involved parties know that the requirements change within the cycles.
Gregor Gisler-Merz Definition of Refactoring : Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. Its heart is a series of small behavior preserving transformations. Each transformation (called a 'refactoring') does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it's less likely to go wrong. The system is also kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring.
Gregor Gisler-Merz A sample: Situation before refactoring: –No OO design –Complete logic in Customer.statement() –Not possible to reuse functionality if adding a html report –A lot of case and if statements due to the lack of inheritance and polymorphism Situation after refactoring: –After a series of refactorings the classes and its methods are small and good readable –Extensible code –Things are done in the appropriate objects Initial Class diagram movie sample Class diagram movie sample after refactoring
Gregor Gisler-Merz General rules of thumb If you have to add something to your program and the structure does not allow a plain-vanilla implementation refactor the code so the change can be done easily afterwards. Upfront refactoring ensure that a profound set of test cases is available. Refactor the code in small overseeable steps. Possible bugs can then easily be found. Kent Beck's metaphor of the two hats: –Add functionality -> Don't change existing code just add new features and tests –Refactoring -> no new functionality, usually no new tests Don Roberts: If you do a thing for the first time then do it. If you do something similar the second time you hesitate but do it once again. If you do it for the third time: refactor it!
Gregor Gisler-Merz Refactoring categories according to Fowler: Composing methods: Extract Method, Inline Temp, Replace Temp with Query Moving Features Between Objects: Move Method, Extract Class, Remove Middle Man Organizing Data: Self Encapsulate Field, Replace Type Code with Class, Replace Array with Object Simplifying Conditional Expressions: Introduce Null Object, Decompose Conditional Making Method Calls Simpler: Rename Method, Add Parameter, Replace Error Code with Exception Dealing with Generalization: Pull Up Field, Extract Interface, Form Template Method
Gregor Gisler-Merz Where do I find a catalog for common refactorings?: 72 Refactorings are described on Martin Fowler's Page –Add ParameterAdd Parameter –Change Bidirectional Association to UnidirectionalChange Bidirectional Association to Unidirectional –Change Reference to ValueChange Reference to Value –Change Unidirectional Association to BidirectionalChange Unidirectional Association to Bidirectional –Change Value to ReferenceChange Value to Reference –Collapse HierarchyCollapse Hierarchy –Consolidate Conditional ExpressionConsolidate Conditional Expression –Consolidate Duplicate Conditional FragmentsConsolidate Duplicate Conditional Fragments –Convert Dynamic to Static Construction by Gerard M. DavisonConvert Dynamic to Static Construction –Convert Static to Dynamic Construction by Gerard M. DavisonConvert Static to Dynamic Construction –Decompose ConditionalDecompose Conditional –Duplicate Observed DataDuplicate Observed Data –Eliminate Inter-Entity Bean Communication (Link Only)Eliminate Inter-Entity Bean Communication –Encapsulate CollectionEncapsulate Collection –Encapsulate Downcast..... see in the Internet for the restEncapsulate Downcast
Gregor Gisler-Merz What indicators are there for a refactoring? Bad smells (1) : Feature envy: a method seems more interested in a class other than the one it is actually in e.g., invoking lots of get methods -> can use Move Method and Extract Method Data clumps: groups of data appearing together in the fields of classes, parameters to methods, etc. e.g., int x, int y, int z -> move these groups into their own class. One can use Extract Class and Introduce Parameter Example: group (start: Date, end: Date) into (aRange: RangeDate) Primitive obsession: using the built-in types of the language too much reluctance to use small objects for small tasks e.g., zip code string -> use objects for individual data values. One can use Replace Data Value with Object
Gregor Gisler-Merz Bad smells continued (2) Switch statements: consider using polymorphism instead e.g., conditionals on type codes defined in other classes. One can use Extract Method (on the switch), Move Method, Replace Type Code, and Replace Conditional with Polymorphism Speculative generality: I think we might need this someday. e.g., abstract classes without a real purpose e.g., unused parameters. One can use Collapse Hierarchy and Remove Parameter Message chains: long chains of navigation to get to an object e.g., client object talks to server object that delegates to another object that the client object must also know about. One can use Hide Delegate
Gregor Gisler-Merz Bad smells continued (3) Middle man: a class that delegates many methods to another class can use Remove Middle Man or Replace Delegation with Inheritance. Attention could also be a legitimate adapter Don t stand so close: two classes that depend too much on each other, with lots of bidirectional communication. Separate the two classes. One can use Move Method, Move Field, and Extract Class (factor out commonality) Alternative classes with different interfaces: methods that do the same thing but have different signatures e.g., put() versus add(). One can use Rename Method
Gregor Gisler-Merz Bad smells continued (4) Refused bequest: when a subclass inherits something that is not needed when a superclass does not contain truly common state/behaviour. One can use Push Down Method and Push Down Field or one can use Replace Inheritance with Delegation (e.g., Square versus Rectangle) Comments: often deodorant for bad smelling code. Refactor code so that the comment becomes extraneous
Gregor Gisler-Merz When is the right time for a refactoring? Whenever you see something that isn't solved adequately Don Roberts "Rule of three" "Once and only once principle" Before adding new functionality While fixing errors During and after Code Reviews If in doubt discuss it with your peers!
Gregor Gisler-Merz Bigger refactorings Big refactorings denote larger refactoring tasks like changing a large inheritance hierarchy Reasons implying bigger refactorings: – Developer missed smaller refactorings (cumulation) – Slow creep in of architectural smells – New features – technology changes (would be low on good software) Differentiation between: –Elementary Refactoring -> Fowler 99 –Big Refactorings -> Rule of thumb: Effort more than one day, affect the whole project team and cannot be implemented by elementary safe refactorings. There are additional unsafe modifications needed
Gregor Gisler-Merz Architectural Smells: indicators for bigger refactorings! Static relation usage (dynamic/runtime relation usage) –Unused classes –Tree like usage –Static cycles Smells in the inheritance hierarchy –InstanceOf calls and missing usage of polymorphism –List like inheritance hierarchies – Speculative generality smell –Subclasses redefine no methods –Parallel inheritance hierarchies –Inheritance hierarchies that are too deep Smells in packages –Unused packages, cycles, packages too small, packages too big –No appropriate hierarchy (deep versus shallow)
Gregor Gisler-Merz Architectural Smells (2) Smell in subsystems –No subsystems, too little subsystems, too large subsystems (more than 100 packages) –Too many subsystems –By-pass subsystem API –Cycles in subsystems Smells in layers –No layering –Upward references (cycles between the layers) –Breach of layers –Too many layers –References between vertical separated layers
Gregor Gisler-Merz References : Fowlers refactoring homepage: Refactorings to patterns: Bad smells homepage: Movie refactoring sample: es/Refactoring-Example.pdf es/Refactoring-Example.pdf Refactoring M. Fowler et al. Addison-Wesley, 1999