76
Jun 14, 2 022 Design Patterns and Refactoring CSC 2053

17-Oct-15 Design Patterns and Refactoring CSC 2053

Embed Size (px)

Citation preview

Page 1: 17-Oct-15 Design Patterns and Refactoring CSC 2053

Apr 21, 2023

Design Patterns and Refactoring

CSC 2053

Page 2: 17-Oct-15 Design Patterns and Refactoring CSC 2053

2

Buzzwords

Design Patterns describe the describe the higher-level organizatihigher-level organization on of of solutions to common problemssolutions to common problems

Design Patterns are a current hot topic in O-O design

Page 3: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 4: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 5: 17-Oct-15 Design Patterns and Refactoring CSC 2053

5

UML

UML stands for Unified Modeling Language

UML is a complicated diagramming language designed for Object-Oriented programming

Page 6: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 7: 17-Oct-15 Design Patterns and Refactoring CSC 2053

7

UML class diagrams

Key: + means public visibility # means protected visibility - means private visibility <blank> 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:

Page 8: 17-Oct-15 Design Patterns and Refactoring CSC 2053

8

UML relationships

A

B

Class Bextendsclass A

C

D1..4

Class Ccontains

1 to 4 objectsof class D

Factory

Product

creates

Other kinds ofrelations

A

B

Class Bimplement

sinterface A

Page 9: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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.

Page 10: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 11: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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();

Page 12: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 13: 17-Oct-15 Design Patterns and Refactoring CSC 2053

NULL OBJECTS

If you use these types of objects a lot:

your code can end up cluttered with tests for null

We need a We need a Design Pattern Design Pattern to help usto help us

13

Page 14: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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 Inhabitants

So class Inhabitant can be sub classed by

BigFish, LittleFish, Algae, and NothingButWater

Page 15: 17-Oct-15 Design Patterns and Refactoring CSC 2053

INHABITANTS HIERARCHY

151515

ABSTRACT CLASS INHABITANTS

LittleFish Big FishNothing 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

Page 16: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 17: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 18: 17-Oct-15 Design Patterns and Refactoring CSC 2053

Refactoring

Changing working code is hazardous —

you introduce bugs that it can take days to find .

Solution: REFACTOR

18

Page 19: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 20: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 21: 17-Oct-15 Design Patterns and Refactoring CSC 2053

21

Source Class

<<abstract>>isNull()

Null Class

isNull()return True

IsNull() as an Abstract method

isNull()return False

Normal class

Page 22: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 23: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 24: 17-Oct-15 Design Patterns and Refactoring CSC 2053

24

Introduce Null Object: In detail, II1. 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.

Page 25: 17-Oct-15 Design Patterns and Refactoring CSC 2053

25

Source Class

<<abstract>>isNull()

Null ClassNull Class

isNull()return True

isNull()Return False

Normal class

INSTEAD OF THIS CODE :

isNull()Return False

Normal class

Page 26: 17-Oct-15 Design Patterns and Refactoring CSC 2053

26

boolean isNull()return false

isNull()

return “Nothing but Water”

Null ClassNormal Class

isNull()Is inherited

USE AN ALTERNATIVE BEHAVIOR

Page 27: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 28: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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, Otherwise the testing is just too much work, and you won’t do itand you won’t do it

JUnit is a great start

28

Page 29: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 30: 17-Oct-15 Design Patterns and Refactoring CSC 2053

30

BigFish

move()

Fish

<<abstract>>move()

LittleFish

move()

We implement move methods in sub classes which are almost the same

Page 31: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 32: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 33: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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}

Page 34: 17-Oct-15 Design Patterns and Refactoring CSC 2053

34

The Fish refactoring – Phase 1

BigFish

move()

Fish

<<abstract>>move()

LittleFish

move()

Note how first version works:

When a BigFish tries to move,

it overrides the move() method it inherits from Fish

Page 35: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 36: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 37: 17-Oct-15 Design Patterns and Refactoring CSC 2053

37

The Fish refactoring: Phase II

BigFish

move()

Fish

<<abstract>>move()

LittleFish

move()

BigFish

okToMove(locn):boolean

Fishmove() : implemented

<<abstract>>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

BigFish and LittleFish implement their own okToMove(locn)

Page 38: 17-Oct-15 Design Patterns and Refactoring CSC 2053

38

Template Design

The Design Pattern is called “Template Method”;

The refactoring is called “Form Template Method”.

Page 39: 17-Oct-15 Design Patterns and Refactoring CSC 2053

39

Problem: Constructors create objects

Constructors make objects.Constructors make objects.

OnlyOnly constructors can make objects constructors can make objects..

When you call a constructor of a class, When you call a constructor of a class,

you you willwill get an get an instance of that class.instance of that class.

Page 40: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 41: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 42: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 43: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 44: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 45: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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 classpublic static Singleton getInstance()

{ return instance; } ...}

See Dice.java

Page 46: 17-Oct-15 Design Patterns and Refactoring CSC 2053

46

Singleton Design Patternpublic class Dice{

// static reference dice identifies the single class instanceprivate 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 classprivate Dice(){

// create a random number generatorrnd = new Random();

}

Page 47: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 48: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 49: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 50: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 51: 17-Oct-15 Design Patterns and Refactoring CSC 2053

51

immutable objects

Example: Strings 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

Page 52: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 53: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 54: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 55: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 56: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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); } ...}

Page 57: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 58: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 59: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 60: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 61: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 62: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 63: 17-Oct-15 Design Patterns and Refactoring CSC 2053

63

When to refactor

You should not refactor:

Stable code (code that won’t ever need to change)

Page 64: 17-Oct-15 Design Patterns and Refactoring CSC 2053

64

The refactoring environment

“Agile” software engineering is based on different assumptions:

Requirements (and therefore design) change as users become acquainted with the software

Page 65: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 66: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 67: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 68: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 69: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 70: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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"; } }}

Page 71: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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"; }}

Page 72: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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)

Page 73: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 74: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 75: 17-Oct-15 Design Patterns and Refactoring CSC 2053

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

Page 76: 17-Oct-15 Design Patterns and Refactoring CSC 2053

76

The End