Download presentation
Presentation is loading. Please wait.
Published byAlban Wilson Modified over 9 years ago
1
17-Oct-15 Design Patterns and Refactoring CSC 2053
2
2 Buzzwords describe the higher-level organization of solutions to common problems Design Patterns describe the higher-level organization of solutions to common problems Design Patterns are a current hot topic in O-O design
3
Design Patterns Design Patterns are always described in UML notation UML is a diagramming language designed for Object-Oriented programming Design Patterns are used to describe refactorings 3
4
4 Design Patterns Refactoring is restructuring code in a series of small, semantics-preserving transformations (i.e. keeps the code working) in order to make the code easier to maintain and modify Refactoring often modifies or introduces Design Patterns
5
5 UML UML stands for Unified Modeling Language UML is a complicated diagramming language designed for Object-Oriented programming
6
UML UML comprises at least seven or eight different kinds of diagrams that can be used to describe: the organization of a program how a program executes how a program is used how a program is deployed over a network …and more We will just deal here with the class diagram 6
7
7 UML class diagrams Key: + means public visibility # means protected visibility - means private visibility means default (package) visibility static variables are underlined Name of the class Variables [optional] Methods Card cardId:int -copy:boolean=false «constructor» Card(int id) +isKind(desiredKind:int) +isSharable():boolean +toString():String Example:
8
8 UML relationships A B Class B extends class A C D 1..4 Class C contains 1 to 4 objects of class D Factory Product creates Other kinds of relations A B Class B implements interface A
9
9 Design patterns It is important in designing object-oriented software that you develop: a good design, that is reusable, able to address future problems.
10
Design Patterns Many design patterns have been established over the years that can help you do this. Good software developers reuse solutions that have worked in the past. So you will find recurring patterns of classes and communicating objects in many systems. 10
11
11 Problem I: Uncertain delegation Through polymorphism you can just send a message to an object, and the object does the right thing, depending on its type However, you must check first that the object is not null: if (myObject != null) myObject.doSomething();
12
EXAMPLES OF NULL OBJECTS You have an Ocean, represented by a sparse array containing a few Fish You have a TrafficGrid, some cells of which contains Cars and Trucks and others are empty You want to send output to some port potentially closed 12
13
NULL OBJECTS If you use these types of objects a lot: your code can end up cluttered with tests for null We need a Design Pattern to help us We need a Design Pattern to help us 13
14
14 Solution: Null Object So create another kind of object, a “null object,” representing the absence of any other kind of object E. G.: An Ocean might contain Inhabitant s So class Inhabitant can be sub classed by BigFish, LittleFish, Algae, and NothingButWater
15
INHABITANTS HIERARCHY 15 ABSTRACT CLASS INHABITANTS LittleFish Big Fish Nothing But Water abstract void REPRODUCE(); REPRODUCE() { SOME CODE} REPRODUCE() { SOME CODE}; REPRODUCE() ; NO CODE; So class Inhabitant has three sub classes: 1. BigFish, 2. LittleFish, Algae, and NothingButWater
16
Null Object This way, no location in the Ocean is null If Inhabitant class contains a method reproduce(), the subclass NothingButWater could implement this method with an empty method body 16
17
17 Refactoring: Introduce Null Objects The general idea is simple, refactor: Instead of having some variables (locations in the array) be null, have them be “null objects” However, this may require numerous changes in the code
18
Refactoring Changing working code is hazardous — you introduce bugs that it can take days to find. Solution: REFACTOR 18
19
19 Refactoring: to make it safe do: Refactoring has three pillars: Introduce the Null Objects in small steps, having an automated set of unit tests, and running unit tests frequently, so when errors occur you can pinpoint it immediately
20
20 Introduce Null Object: In detail 1.Create a subclass of the source class to act as a null version of the class. Create an isNull() method in the source class and in the Null class. For the source class it should return false, for the Null class it should return true. All other subclasses of the source will inherit its version
21
21 Source Class > isNull() Null Class isNull() return True IsNull() as an Abstract method isNull() return False Normal class
22
22 IsNull() is not an abstract method Source Class boolean isNull() return false Null Class isNull() return True isNull() Is inherited Normal class The Null class overrides the parent class isNull() method
23
Null Object After creating the Null Object class: 1.Compile. 2.Find all places that produce a null when asked for a source object. 3.Replace them with a null object instead. 4.Replace one source and its clients at a time 23
24
24 Introduce Null Object: In detail, II 1.When the result is null for an operation, 2.do some alternative behavior instead. 3.E.g. return a string “Nothing But Water”. 4.For each of these cases, override the operation in the Null class with the alternative behavior. 5.Compile, and test.
25
25 Source Class > isNull() Null Class isNull() return True isNull() Return False Normal class INSTEAD OF THIS CODE : isNull() Return False Normal class
26
26 boolean isNull() return false isNull() return “Nothing but Water” Null Class Normal Class isNull() Is inherited USE AN ALTERNATIVE BEHAVIOR
27
27 Refactoring details IMPORTANT: Use “baby steps” Do the refactoring a little at a time, so that it’s very easy to catch and correct errors
28
USE TESTS THAT ARE already implemented. IMPORTANT - USE a good set of : totally automated tests — already implemented. Otherwise the testing is just too much work, and you won’t do it Otherwise the testing is just too much work, and you won’t do it JUnit is a great start 28
29
29 Scenario: 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
30
30 BigFish move() Fish > move() LittleFish move() We implement move methods in sub classes which are almost the same
31
31 Problem: Similar methods in subclasses We have a Fish class with two subclasses, BigFish and LittleFish The two kinds move the same way except a little fish won’t move near a big fish. To avoid code duplication, the move method ought to be in the superclass Fish
32
Problem - Since a LittleFish won’t move to some locations where a BigFish will move The test for whether it is OK to move really ought to be in the move method in the sub classes More generally, you want to have almost the same method in two or more sibling classes 32
33
33 The move() method General outline of the method: public void move() { choose a random direction; // same for both find the location in that direction; // same for both check if it’s ok to move there; // different if it’s ok, make the move; // same for both }
34
34 The Fish refactoring – Phase 1 BigFish move() Fish > move() LittleFish move() Note how first version works: When a BigFish tries to move, it overrides the move() method it inherits from Fish
35
35 Solution - Template Method Write the oktoMove() method as an abstract method in the super class This in turn requires that the super class be abstract
36
ReFactoring -step by step To refactor: 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() differently in each subclass 36
37
37 The Fish refactoring: Phase II BigFish move() Fish > move() LittleFish move() BigFish okToMove(locn):boolean Fish move() : implemented > okToMove(locn):boolean LittleFish okToMove(locn):boolean Note how revised version works: When a BigFish tries to move, it uses the move() method it inherits from Fish Big Fish and LittleFish implement their own okToMove(locn)
38
38 Template Design The Design Pattern is called “Template Method”; The refactoring is called “Form Template Method”.
39
39 Problem: Constructors create objects Constructors make objects. Constructors make objects. Only constructors can make objects. Only constructors can make objects. When you call a constructor of a class, When you call a constructor of a class, you will get an instance of that class. you will get an instance of that class.
40
Singleton classes Sometimes you want more flexibility than that— YOU WANT TO: o guarantee that you can never have more than one object of a given class o create an object only if you don’t already have an equivalent object o create an object without being sure exactly what kind of object you want 40
41
41 Constructors Although only constructors make objects You don’t have to call constructors directly— you can call a method that calls the constructor for you Several “creational” Design Patterns are based on this observation
42
42 Singleton Design Pattern – A Creational Design Pattern A Singleton Design pattern provides a model for building a class that can have only one instance You may want just one instance of a null object, which you use in many places Or - to create just one AudioStream, so you can only play one tune at a time
43
Singleton Design Pattern You may want to have many printers but one spooler You might want to have one copy of alphabetic characters in a document 43
44
Singleton classes The method : define a static reference variable of the class type which serves as the single instance of the class. Make the constructor private so a user can not instantiate the class Use a static public method that returns the reference to the object 44
45
45 Singleton Design Pattern class Singleton { // create a single instance of the class private static Singleton instance = new Singleton(); // don’t let Java give you a default public constructor private Singleton() { } // Have a method that returns the instance of the class public static Singleton getInstance() { return instance; }... } See Dice.java
46
46 Singleton Design Pattern public class Dice { // static reference dice identifies the single class instance private static Dice dice = null; private Random rnd; private int dice1, dice2; // private constructor is called by the method getDice() // to create a single instance of the class private Dice() { // create a random number generator rnd = new Random(); }
47
47 Singleton Design Pattern /* if no object currently exists, the method calls the private constructor to create an instance; if an object exists, method returns the static reference variable*/ public static Dice getDice() { if (dice == null) { dice = new Dice(); // create a single dice object } return dice; // the single reference to the class } // other methods in the class
48
48 The Factory Method Design Pattern Suppose you write a class that works with several different kinds of objects You can do this if the classes all have a common interface You may want to be able to create objects, without being dependent on the kind of object A factory method can create instances of different classes, depending (say) on its parameters
49
FACTORY DESIGN PATTERN Example: public Image createImage (String ext) { if (ext.equals("gif")) return new GIFImage(); if (ext.equals("jpg")) return new JPEGImage();... } Note that it is not known beforehand what KIND of object will be created. 49
50
50 The Immutable Design Pattern There are many benefits to objects that cannot be changed after they have been created Such objects are called immutable Objects that refer to an immutable object never have to worry about whether that object has been changed Immutable objects are thread-safe—this is a significant efficiency concern, because synchronization is expensive
51
51 immutable objects Example: String s in Java It’s easy to make immutable objects in Java: Make all instance variables private, and Provide no methods that change those variables. Therefore you will have no setter methods
52
52 Delegation (or, when not to use inheritance) When you create a subclass, you agree to inherit all its (non-private) fields and methods What if you don’t want them all? Example: A Vector can do everything that a Stack should be able to do, and much, much more
53
Delegation You may want to inherit just some of the functionality, and probably add some of your own Inheritance doesn’t let you do that—at least, not easily 53
54
54 Delegation If your class wants to hide variables or methods inherited from a superclass, it shouldn’t inherit from that superclass If an object needs to be a different subclass at different times (say, a LittleFish turning into a BigFish), then it shouldn’t be a subclass of that class in the first place
55
Delegation Instead of inheriting, just use an instance of that class, and delegate to it what needs to be done In the next example, the stack class should not extend Vector – why???? 55
56
56 Example: Stacks class Stack { Vector contents = new Vector(); public void push(Object o) { contents.add(o); // delegate to the Vector } public Object pop() { return contents.remove(contents.size() – 1); }... }
57
57 Design vs. coding “Design” is the process of determining, in detail, what the finished product will be and how it will be put together “Coding” is following the plan
58
DESIGN In traditional engineering (building bridges), design is perhaps 15% of the total effort In software engineering, design is 85-90% of the total effort By comparison, coding is cheap 58
59
59 The refactoring environment Traditional software engineering is modeled after traditional engineering practices (design first, then code) Assumption: The desired end product can be determined in advance
60
60 Refactoring Refactoring is: restructuring (rearranging) code......in a series of small, semantics-preserving changes (i.e. the code keeps working)......in order to make the code easier to maintain and modify
61
61 Refactoring 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
62
62 When to refactor When can you refactor? You should be in a supportive environment (agile programming team, or doing your own work) You should have an adequate set of automatic unit tests
63
63 When to refactor You should not refactor: Stable code (code that won’t ever need to change)
64
64 The refactoring environment “Agile” software engineering is based on different assumptions: Requirements (and therefore design) change as users become acquainted with the software
65
65 The refactoring environment Refactoring is fundamental to agile programming Refactoring is necessary when the design is found to be flawed It is advisable to refactor the design - designs should not be immutable
66
66 A personal view Design, because it is a lot more creative than simple coding, is also a lot more fun Most programming methodologies do not encourage good programming. Very good programmers find ways to improve the methodologies
67
67 Back to refactoring When should you refactor? Any time you find that you can improve the design of existing code You detect a “bad smell” (an indication that something is wrong) in the code
68
Refactor? When can you refactor? You should be in a supportive environment (agile programming team, or doing your own work) You should have an adequate set of automatic unit tests 68
69
69 Example 1: switch statements switch statements are very rare in properly designed 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 The simplest refactoring for this case is the creation of subclasses
70
70 Example 1, 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"; } } }
71
71 Example 1, improved 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"; } }
72
72 How is this an improvement? 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, we’ve separated them out (so no more switch statements)
73
REfactoring We’ve gotten rid of the flags we needed to tell one kind of animal from another We’re now using Objects the way they were meant to be used 73
74
74 JUnit tests As we refactor, we need to run JUnit tests to ensure that we haven’t introduced errors, e.g. 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
75
75 Bad Smell Examples We should refactor any time we detect a “bad smell” in the code Examples of bad smells include: Duplicate Code Long Methods Large Classes Long Parameter Lists Multi -location code changes
76
76 The End
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.