290 likes | 447 Views
Refactoring to Patterns. Ravindra Chilaka Extreme Programming Seminar XP 2004 b. Contents. Patterns Introduction. - What is a pattern? - What makes a pattern? - Why use pattern? Refactoring. - Refactoring. - Why Refactoring to patterns? - When to Refractor?
E N D
Refactoring to Patterns Ravindra Chilaka Extreme Programming Seminar XP 2004 b
Contents • Patterns Introduction. - What is a pattern? - What makes a pattern? - Why use pattern? • Refactoring. - Refactoring. - Why Refactoring to patterns? - When to Refractor? - Example : Switch Statement. - The Refactoring Environment. • Examples of Refactoring to Patterns. - Extract Adapter. - The Template Method. (continued..)
Contents - Encapsulate Classes with Factory. - Encapsulate Composite with Builder. - Move Embellishment to Decorator. - Replace Conditional Logic with Stratergy. - Replace Hard-Coded Notifications with Observer. • Summary. • Questions ??
Patterns Introduction What is a Pattern: (Definition) • Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.(C. Alexander, “The Timeless Way of Building”, 1979) • „A pattern is a named nugget of insight that conveys the essence of a proven solution to a recurring problem within a certain context amidst competing concerns.“ (Brad Applenton, „Patterns and Software : Essential Concepts and Terminology“,2000)
Patterns Introduction • Patterns describe common ways of doing things. • They are collected by people who spot repeating themes in designs. • They take each theme and describe it so that others can use it. • They capture the static and dynamic structures and collaborations of successful solutions to problems that arise when building applications in a particular domain. • A Pattern is much more than a Model. • Design Patterns describe good solutions to common (or at least, not extremely rare) design problems: • Design Patterns are always specified in UML. • Model-View-Controller is a very common Design Pattern. • Design Patterns describe the higher-level organization of solutions to common problems.
Patterns Introduction • What makes a pattern? • Solve a problem (It must be useful) • Have a Context (Where it can be used) • Recur (Must be relevant in other situations) • Teach (provide sufficient understanding to tailor the solution) • Name (To refer Consistently).
Patterns Introduction • Why use Patterns? Patterns help you learn from other’s successes, instead of your own failures(Mark Johnson (cited by B. Eckel)) • An additional layer of abstraction • separate things that change from things that stay the same • distilling out common factors between a family of similar problems • similar to design • Insightful and clever way to solve a particular class of problems • most general and flexible solution
Refactoring • Refactoring is rearranging existing code while maintaining the same functionality • Refactoring is usually done in terms of applying some Design Pattern • Refactoring is restructuring code in a series of small, semantics-preserving transformations (i.e. the code keeps working) in order to make the code easier to maintain and modify • Refactoring often modifies or introduces Design Patterns • Refactoring is not just any old restructuring • You need to keep the code working • You need small steps that preserve semantics • You need to have unit tests to prove the code works • There are numerous well-known refactoring techniques • You should be at least somewhat familiar with these before inventing your own
Refactoring • Why Refactoring to Patterns ? • To reduce or remove Duplication. • Simplifying the unsimple. • Make our code better at its intention.
Refactoring • When to Refactor ? • You should refactor: • Any time that you see a better way to do things • “Better” means making the code easier to understand and to modify in the future • You can do so without breaking the code • Unit tests are essential for this • You should not refactor: • Stable code (code that won’t ever need to change) • Someone else’s code • Unless you’ve inherited it (and now it’s yours)
Example : switch statements • switch statements are very rare in properly designed object-oriented code. • Therefore, a switch statement is a simple and easily detected “bad smell”. • Of course, not all uses of switch are bad. • A switch statement should not be used to distinguish between various kinds of object. • There are several well-defined refactorings for this case • The simplest is the creation of subclasses.
Example, continued • class Animal { final int MAMMAL = 0, BIRD = 1, REPTILE = 2; int myKind; // set in constructor ... String getSkin() { switch (myKind) { case MAMMAL: return "hair"; case BIRD: return "feathers"; case REPTILE: return "scales"; default: return "integument"; } }}
Example, continued • class Animal { String getSkin() { return "integument"; }}class Mammal extends Animal { String getSkin() { return "hair"; }}class Bird extends Animal { String getSkin() { return "feathers"; }}class Reptile extends Animal { String getSkin() { return "scales"; }}
How is this an improvement? • Adding a new animal type, such as Amphibian, does not require revising and recompiling existing code. • Mammals, birds, and reptiles are likely to differ in other ways, and we’ve already separated them out (so we won’t need more switch statements). • We’ve gotten rid of the flags we needed to tell one kind of animal from another. • Basically, we’re now using Objects the way they were meant to be used.
JUnit tests • As we refactor, we need to run JUnit tests to ensure that we haven’t introduced errors • public void testGetSkin() { assertEquals("hair", myMammal.getSkin()); assertEquals("feathers", myBird.getSkin()); assertEquals("scales", myReptile.getSkin()); assertEquals("integument", myAnimal.getSkin());} • This should work equally well with either implementation • The setUp() method of the test fixture may need to be modified
The Refactoring Environment • Traditional software engineering is modeled after traditional engineering practices (= design first, then code) • Assumptions: • The desired end product can be determined in advance • Workers of a given type (plumbers, electricians, etc.) are interchangeable • “Agile” software engineering is based on different assumptions: • Requirements (and therefore design) change as users become acquainted with the software • Programmers are professionals with varying skills and knowledge • Programmers are in the best position for making design decisions • Refactoring is fundamental to agile programming • Refactoring is sometimes necessary in a traditional process, when the design is found to be flawed
Examples of Refactoring to Patterns • Extract Adapter • The Template Method • Encapsulate Classes with Factory • Encapsulate Composite with Builder • Move Embellishment to Decorator • Replace Conditional Logic with Strategy • Replace Hard-Coded Notifications with Observer
Examples of Refactoring to Patterns • Extract Adapter: One Class adapts multiple versions of component, library, API or other Entity. • Solution: Extract an Adapter for a single version of the component , library, API or other Entity.
Examples of Refactoring to Patterns • The Template Method: • Template Methods lead to an inverted control structure • A superclass calls methods in its subclass • Template methods are so fundamental that they can be found in almost every abstract class • Template Method uses inheritance • A similar pattern, Strategy Pattern, uses delegation rather than inheritance
Examples of Refactoring to Patterns • Example : Big fish and little fish • The scenario: “big fish” and “little fish” move around in an “ocean” • Fish move about randomly • A big fish can move to where a little fish is (and eat it) • A little fish will not move to where a big fish is
Examples of Refactoring to Patterns • General outline of the method: • public void move() {choose a random direction; // same for bothfind the location in that direction; // same for bothcheck if it’s ok to move there; // differentif it’s ok, make the move; // same for both} • Solution: • Extract the check on whether it’s ok to move • In the Fish class, put the actual (template) move() method • Create an abstract okToMove() method in the Fish class • Implement okToMove() in each subclass
Note how this works: When a BigFish tries to move, it uses the move() method in Fish But the move() method in Fish uses the okToMove(locn) method in BigFish And similarly for LittleFish Fish Fish move() <<abstract>>okToMove(locn):boolean <<abstract>>move() BigFish BigFish LittleFish BigFish okToMove(locn):boolean move() okToMove(locn):boolean move() Examples of Refactoring to Patterns
Examples of Refactoring to Patterns • Encapsulates Classes with Factory: Clients directly instantiate classes that reside in one package and implement a common interface. • Solution: Make the class constructors non-public and let clients creates instances of them using a factory.
Types of Refactoring to Patterns • Encapsulate Composite with Builder: Building a composite is repititive , complicated or error-prone. • Solution: Simplify the build by letting the Builder handles the details.
Examples of Refactoring to Patterns • Embellishment to Decorator: Code Provides an embellishment to a class‘ core responsibility. • Solution: Move the embellishment code to a Decorator.
Examples of Refactoring to Patterns • Replace Conditional Logic with Stratergy: conditional logic in a method controls which of several variants of a calculation are executed. • Solution: create a Stratergy for each variant and make the method delegate the caliculation to a single stratergy.
Examples of Refactoring to Patterns • Replace Hard-Coded Notifications with Observer: Subclasses are hardcoded to notify a single instance of another class. • Solution: Remove the subclasses by making their superclass capable of notifying one or more instances of any classes that implement a observable interface.
Summary • Patterns describe common ways of doing things. They are collected by people who spot repeating themes in designs. • We should refactor any time we detect a “bad smell” in the code. Refactoring makes code easier to understand, maintain and modify. • Various Refactoring techniques to the patterns can be applied to the real-world problems depending up on the application criteria and usage of the system. (For e.g.: Observer ,Strategy and Template Patterns, etc. are most commonly used).
Questions ?? ? ? ?