670 likes | 835 Views
Refactoring. Motto. „Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”. Martin Fowler. Contents. First rendezvous TDD and continues refactoring Why should you refactor? When s hould y ou r efactor? Strategy
E N D
Motto „Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” Martin Fowler
Contents • First rendezvous • TDD and continues refactoring • Why should you refactor? • When should you refactor? • Strategy • Bad smells in code • Low level refactoring technics • Refactoring to design patterns
A bit of propaganda • Programs that are hard to read are hard to modify. • Programs that have duplicated logic are hard to modify. • Programs that require additional behavior that requires you tochange running code are hard to modify. • Programs with complex conditional logic are hard to modify. • Any fool can write code that a computer can understand. Good programmers writecode that humans can understand.
Symptoms • Rigidity • Fragility • Immobility • Viscosity • Needless Complexity • Needless Repetition • Opacity
Bad smells • Duplicated code • Long method • Large class • Too many parameters. • Feature envy. • Inappropriate intimacy • Refused bequest: • Lazy class / Freeloader: • Contrived complexity • Excessively long identifiers • Excessively short identifiers • Excessive use of literals. • Ubercallback
Bad stinks If it stinks, change it Kent’s Beck grandmaabout babies’ diapper change strategy
First rendezvous • Refactoring: a change made to the internal structure of software to make iteasier to understand and cheaper to modify without changing its observable behavior.
TDD and continues refactoring • RedYou create a test that expresses what you expect your code to do. The test fails (turns red) because you haven't created code to make the test pass. • Green You program whatever is expedient to make the test pass (turn green). You don't pain yourself to come up with a duplication-free, simple, clear design at this point. You'll drive towards such a design later, when your test is passing and you can comfortably experiment with better designs. • RefactorYou improve the design of the code that passed the test.
TDD and continues refactoring • Keep defect counts low • Refactor without fear • Produce simpler, better code • Program without stress
Why should you refactor? • To make easier put in new code. • To improve the design of software. • To make software easier to undersand. • To make development more nice. • It helps you to find bugs. • It helps you develop code more quickly.
When should you refactor? • The rule of three The first time you do something, you just do it. Thesecond time you do something similar, you wince at the duplication, but you do the duplicate thinganyway. The third time you do something similar, you refactor.
When should you refactor? • Refactor when you add function.Refactor to help you understand some code you need to modify.Refactor to change design that does not help you to add new feature easy. • Refactor when you need to fix a bug. • Refactor as you do a code review. Design/TechnicalDebt metphore and when shouldn’t you refactor.
Refactoring and design • You still do upfront design, but now you don't try to find thebest solution. Instead all you want is a reasonable solution. You know that as you build the solution, as you understand more about the problem, you realize that the best solution is different from the one you originally came up with. With refactoring this is not a problem, for it no longer is expensive to make the changes.
Refactoring and design • Overdesign – design that is more flexible than you need, it make project bigger and more complex. • Flexibility cost. With refactoring we move toward simplicity of design rather than flexibility. • You don’t know now which changes you will need introduce to system in the future. Wrong predicted direction of changes make some flexibility newer used. • Emphasis on: how difficult is it going to be to refactor a simple solution into the flexible solution? • Consciousness what you can refactor easy.
So what is the strategy • When you find you have to add a feature to a program, and the program's code is notstructured in a convenient way to add the feature, first refactor the program to make it easy to add the feature, then add the feature. • Before you start refactoring, check that you have a solid suite of tests. Thus the refactoring should be self-checking. • Refactor the program in small steps. If you make a mistake, it is easy to find the bug. • Don’t optimize ahead, concentrate on simplicity of design. Wait for result from diagnostics tools for optimization.
Bad smells in code • Duplicated codeExtract method, Extract class, Form Template Method, Introduce Polymorphic Creation with Factory Method, Chain Constructors,Replace One/Many Distinctions with Composite,Extract Composite, Unify Interfaces with Adapter, Introduce Null Object. • Long MethodExtract method, Introduce parameter object, Compose Method, Move Accumulation to Collecting Parameter, Replace Conditional Dispatcher with Command, Move Accumulation to Visitor,Replace Conditional Logic with Strategy
Bad smells in code • Conditional complexityReplace Conditional Logic with Strategy, Move Embellishment to Decorator, Replace State-Altering Conditionals with State, Introduce Null Object. • Primitive obsessionReplace data value with object, Replace type code with class, Replace State-Altering Conditionals with State, Replace Conditional Logic with Strategy, Replace Implicit Tree with Composite, Replace Implicit Language with Interpreter, Move Embellishment to Decorator, Encapsulate Composite with Builder
Bad smells in code • Indecent ExposureEncapsulate Classes with Factory • Solution SprawlMove Creation Knowledge to Factory • Alternative classes with different interfacesExtract superclass, Rename method,Unify Interfaces with Adapter • Lazy classCollapse HierarchyInline Singleton • Combinatorial explosionReplace Implicit Language with Interpreter • Oddball solutionUnify Interfaces with Adapter
Bad smells in code • Large classExtract class, Extract subclass, Extract interface,Replace Conditional Dispatcher with Command, Replace State-Altering Conditionals with State, Replace Implicit Language with Interpreter • Switch statementsExtract method, Replace type code with state/strategy, Replace conditional with polimorphism,Replace Conditional Dispatcher with Command, Move Accumulation to Visitor.
Simple refactorings • Support from development environment for automation of refactoring VS/R# • Rename • Encapsulate field • Extract method • Extract Interface • Promote local variable to parameter • Remove parameters • Reorder parameters
Simple refactorings • Make a method (non)static • Push/pull members up/down • Replace constructor with Factory Method • Safe delete • Move type/static method
Self Encapsulate Field • You are accessing a field directly, but the coupling to the field is becoming awkward. • Create getting and setting methods for the field and use only those to access the field. • It allows a subclass to overridehow to get that information with a method and that it supports more flexibility in managing thedata, such as lazy initialization, which initializes the value only when you need to use it. • You are accessing a field in asuperclass but you want to override this variable access with a computed value in the subclass. • Make code more complicated.
Rename method • The name of a method does not reveal its purpose. • Change the name of the method.
Extract method • You have a code fragment that can be grouped together. • Turn the fragment into a method whose name explains the purpose of the method. • Pay attention to naming. • Do it allways when sematic distance between method body and method name is big. • It increases the chances that othermethods can use a method when the method is finely grained • It allows the higher-levelmethods to read more like a series of comments. • Overriding also is easier when the methods arefinely grained.
Extract method • No Local Variables • Using Local Variables (copy vs move) • Reassigning a Local Variable
Extract interface • Several clients use the same subset of a class's interface, or two classes have part of theirinterfaces in common. • Extract the subset into an interface. • Only a particular subset of a class'sresponsibilities is used by a group of clients or class needs to work with any class that canhandle certain requests. • Class has a distinct roles in different situations. Make interface for each role. • You want to specify the outbound of the operations the class makes on its server.
Extract superclass • You have two classes with similar features. • Create a superclass and move the common features to the superclass. • Prevents code duplication by class inherence.
Extract subclass • A class has features that are used only in some instances. • Create a subclass for that subset of features. • Class has behavior used forsome instances of the class and not for others. • Limitations: • You can'tchange the class-based behavior of an object once the object is created. • You can change theclass-based behavior with Extract Class simply by plugging in different components. • You canalso use only subclasses to represent one set of variations. • If you want the class to vary inseveral different ways, you have to use delegation for all but one of them.
Pull up method • You have methods with identical results on subclasses. • Move them to the superclass. • Eliminate code duplication. • If method do the same but have different signatures unify signatures. • If method are similiar but not the same consider template method. • If two methods in different classes can be parameterized in such a way that they end up as essentially the same method then the smallest step is to parameterize each method separately and then generalize them. • If Pull Up Method body refer tofeatures that are on the subclass but not on the superclass consider generalizationthat feature or creation an abstract feature in the superclass.
Move method • A method is, or will be, using or used by more features of another class than the class on which itis defined. • Create a new method with a similar body in the class it uses most. Either turn the old method intoa simple delegation, or remove it altogether. • Do this if class has too much behaviour or when classes are collaborating too much and are too highly coupled.
Move method - mechanics • Examine all features used by the source method that are defined on the source class.Consider whether they also should be moved. • Check the sub- and superclasses of the source class for other declarations of themethod. • Declare the method 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. • Compile the target class. • Determine how to reference the correct target object from the source. • 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. • If you remove the source method, replace all the references with references to the targetmethod. • Compile and test. • R# make method static/move/make method unstatic
Compose method • You can't rapidly understand a method's logic. • Transform the logic into a small number of intention-revealing steps at the same level of detail. • Efficiently communicates what a method does and how it does what it does. • Simplifies a method by breaking it up into well-named chunks of behavior at the same level of detail. • Can lead to an overabundance of small methods. • Can make debugging difficult because logic is spread out across many small methods.
Compose method mechanics • Think small. • Remove duplication and dead code. • Communicate intention. • Simplify. • Use the same level of detail.
Replace Constructors with Creation Methods • There is a number of different, not trivial constructors • Creation Methods have meaningful names • Creation Methods can have identical parameters Mechanics: • Create Gathering Constructor (Chain Constructors) • Compile and Run • Find a constructor Call • Create a proprietary static method • Replace constructor calls with a method • Make the constructor private
Replace inherience with delegation • A subclass uses only part of a superclasses interface or does not want to inherit data. • Create a field for the superclass, adjust methods to delegate to the superclass, and remove thesubclassing. • By using delegation instead, you make it clear that you are making only partial use of thedelegated class. You control which aspects of the interface to take and which to ignore. The costis extra delegating methods that are boring to write but are too simple to go wrong. • You need to use functionality or implementation? • Changing behaviour in runtime by changing reference to object which we use to delegate functionality.
Replace inherience with delegation mechanics • Create a field in the subclass that refers to an instance of the superclass. Initialize it tothis. • Change each method defined in the subclass to use the delegate field. Compile and testafter changing each method. • Remove the subclass declaration and replace the delegate assignment with anassignment to a new object. • For each superclass method used by a client, add a simple delegating method. • Compile and test.
Replace conditional with polymorphism • You have a conditional that chooses different behavior depending on the type of an object. • Move each leg of the conditional to an overriding method in a subclass. Make the original methodabstract. • The biggest gain occurs when this same set ofconditions appears in many places in the program. • Add a new case just by creation a new subclass andprovide the appropriate methods. • Clients of the class don't need to know about the subclasses,which reduces the dependencies.
Replace type code with class • A class has a simple typecode (int, String)that does not affect its behavior. • Replace the number with a new class. • A field's type fails to protect it from unsafe assignments and invalid equality comparisons. • Constrain the assignments and equality comparisons and provides better protection from invalidoperations. • Compilation time detection for invalid operations. • If you replace the number with a class, the compiler can type check on the class. By providingfactory methods for the class, you can statically check that only valid instances are created andthat those instances are passed on to the correct objects. • If type code is not pure data but it affect state consider Replace type code with subclasses or Replace type code with State/Strategy. • Requires more code than using unsafe type does.
Replace type code with subclasses • You have an immutable type code that affects the behavior of a class. • Replace the type code with subclasses by creation a subclass for each type code. • To deal with presence of case-like conditional statements which test the value of the type code and thenexecute different code depending on the value of the type code or presence of features that arerelevant only to objects with certain type codes. • Cooperates with Replace Conditional with Polimorphism, Push Down Method. • Problems when type code changes after the object is created or class with the type code is already subclassed for another reason. In that case consider Replace Type Code with State/Strategy. • It moves knowledge of the variant behavior from clients of the class to the class itself. Adding new variants is simply adding a subclass. Without polymorphism You have to find all the conditionals and change those. So this refactoring is particularly valuable when variants keep changing.
Replace type code with subclassesmechanics • Create a new class for the type code. • Modify the implementation of the source class to use the new class. • Compile and test. • For each method on the source class that uses the code, create a new method that usesthe new class instead. • One by one, change the clients of the source class so that they use the new interface. • Compile and test after each client is updated. • Remove the old interface that uses the codes, and remove the static declarations of thecodes. • Compile and test.
Replace type code with subclasses mechanics • Self-encapsulate the type code. • For each value of the type code, create a subclass. Override the getting method of thetype code in the subclass to return the relevant value. • Compile and test after replacing each type code value with a subclass. • Remove the type code field from the superclass. Declare the accessors for the type codeas abstract. • Compile and test.
Replace Type Code with State/Strategy • You have a type code that affects the behavior of a class, but you cannot use subclassing. • Replace the type code with a state object. • Self-encapsulate the type code. • Create a new class, and name it after the purpose of the type code. This is the stateobject. • Add subclasses of the state object, one for each type code. • Create an abstract query in the state object to return the type code. Create overridingqueries of each state object subclass to return the correct type code. • Compile. • Create a field in the old class for the new state object. • Adjust the type code query on the original class to delegate to the state object. • Adjust the type code setting methods on the original class to assign an instance oftheappropriate state object subclass. • Compile and test.
Composite and test-driven refactoring • Composite refactoring is high level refactoring composed from several simple refactoring. Strongly affect system architecture. • Test-driven refactoring is based on tests replacement of existance code by new code. • Refactoring to pattern – you have to involve all transformation to achieve a genuine improvement. • Refactoring toward pattern – you can stop in the midle of way to a pattern implementation if you find that sufficient design improvement was achaived. • Refactoring from pattern – replace one pattern by another.
Collapse Hierarchy • A superclass and subclass are not very different. • Merge them together. • Choose which class is going to be removed: the superclass or the subclasses. • Use Pull Up Field and Pull Up Method or Push Down Method and Push Down Fieldto move all the behavior and data of the removed class to the class with which it is beingmerged. • Compile and test with each move. • Adjust references to the class that will be removed to use the merged class. This willaffect variable declarations, parameter types, and constructors. • Remove the empty class. • Compile and test.
Inline class • A class isn't doing very much, it has not sense outside the scope of certain class. • Move all its features into another class and delete it. • Declare the public protocol of the source class onto the absorbing class.Delegate allthese methods to the source class. • Change all references from the source class to the absorbing class. • Compile and test. • Use Move Method and Move Field to move features from the source class to theabsorbing class until there is nothing left.
Inline singelton • Code needs access to an object but doesn't need a global point of access toit. • Move the Singleton's features to a class that stores and provides access to the object. Delete the Singleton. • Consider when passing object is easier than access to global object. • Consider if you need access protection rather than existance of one instance of object. • Makes object collaborations more visible and explicit. • Requires no special code to protect a single instance. • Complicates a design when passing an object instance through many layers is awkward or difficult.
Inline singeltonmechanics • Declare the Singleton's public methods on your absorbing class. Make the new methods delegate back to the Singleton, and remove any "static" designations they may have (in the absorbing class).If your absorbing class is itself a Singleton, you'll want to keep the "static" designations for the methods. • Change all client code references to the Singleton to references to the absorbing class. • Use Move Method and Move Field to move features from the Singleton to the absorbing class until there is nothing left.As in first step, if your absorbing class is not a Singleton itself, remove any "static" designations from the methods and fields you move. • Delete the Singleton.
Introduce null object • Logic for dealing with a null field or variable is duplicated throughout your code. • Replace the null logic with a Null Object, an object that provides the appropriate null behavior. • Prevents null errors without duplicating null logic. • Simplifies code by minimizing null tests. • Complicates a design when a system needs few null tests. • Can yield redundant null tests if programmers are unaware of a Null Object implementation. • Complicates maintenance. Null Objects that have a superclass must override all newly inherited public methods.
Introduce null objectmechanics • Create a null object by applying Extract Subclass on the source class or by making your new class implement the interface implemented by the source class. If you decide to make your null object implement an interface, but that interface doesn't yet exist, create it by applying Extract Interface on the source class. • Look for a null check. Overide the invoked method in the null object so it implements the alternative behavior. • Repeat step 2 for other null checks associated with the source class. • Find a class that contains one or more occurrences of the null check and initialize the field orlocal variable that is referenced in the null check to an instance of the null object. Perform this initialization at the earliest possible time during the lifetime of an instance of the class (e.g., upon instantiation).This code should not affect pre-existing code that assigns the field or local variable to an instance of the source class. The new code simply performs an assignment to a null object prior to any other assignments. • In the class you selected in step 4, remove every occurrence of the null check. • Repeat steps 4 and 5 for every class with one or more occurrences of the null check.