Presentation is loading. Please wait.

Presentation is loading. Please wait.

17-Oct-15 Design Patterns and Refactoring CSC 2053.

Similar presentations


Presentation on theme: "17-Oct-15 Design Patterns and Refactoring CSC 2053."— Presentation transcript:

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


Download ppt "17-Oct-15 Design Patterns and Refactoring CSC 2053."

Similar presentations


Ads by Google