+ All Categories
Home > Documents > Chapter 12 Chain of Responsibility

Chapter 12 Chain of Responsibility

Date post: 22-Feb-2016
Category:
Upload: sinjin
View: 43 times
Download: 1 times
Share this document with a friend
Description:
Chapter 12 Chain of Responsibility. Summary prepared by Kirk Scott. Design Patterns in Java Chapter 12 Chain of Responsibility. Summary prepared by Kirk Scott. Introduction Before the Introduction. One general goal of a good object-oriented design is loose coupling - PowerPoint PPT Presentation
Popular Tags:
100
Chapter 12 Chain of Responsibility Summary prepared by Kirk Scott 1
Transcript
Page 1: Chapter 12 Chain of Responsibility

1

Chapter 12Chain of Responsibility

Summary prepared by Kirk Scott

Page 2: Chapter 12 Chain of Responsibility

2

Design Patterns in JavaChapter 12

Chain of Responsibility

Summary prepared by Kirk Scott

Page 3: Chapter 12 Chain of Responsibility

3

Page 4: Chapter 12 Chain of Responsibility

4

Page 5: Chapter 12 Chain of Responsibility

5

Page 6: Chapter 12 Chain of Responsibility

6

Page 7: Chapter 12 Chain of Responsibility

7

Introduction Before the Introduction

• One general goal of a good object-oriented design is loose coupling

• The idea is that changes in one area of the design won’t require changes in other areas

• Encapsulation in Java provides a certain degree of loose coupling

• Client objects are insulated from changes in class implementation code as long as the set of public methods remains the same

Page 8: Chapter 12 Chain of Responsibility

8

• Inheritance, polymorphism, and dynamic binding all in their own way support loose coupling

• Client code doesn’t have to know where in an inheritance hierarchy a method is implemented

• Client code can call methods on superclass references or objects of classes that implement a given interface

Page 9: Chapter 12 Chain of Responsibility

9

• On the other hand, you’ve also seen code where it can be useful to call getClass(), for example, to find out what class an object is an instance of

• The use of that method emphasized this simple fact of programming, which was already clear:

• In order to call a method, in general, you need to know what kind of object you are trying to call it on

Page 10: Chapter 12 Chain of Responsibility

10

• This reflects a kind of coupling:• The client has to know which class/object it’s

working with• Or viewed another way, the client has to know

which class/object actually has the method containing the functionality it wants to use

Page 11: Chapter 12 Chain of Responsibility

11

• This kind of coupling can be loosened in certain kinds of software designs

• For example, the objects in a design may be in some sort of relationship where a call made on one object can be satisfied by a call “through” to another

• In other words, one object will have the method, so that the call can be made on it

• However, the implementation of the method depends on the existence of the functionality in another class that the call is passed to

Page 12: Chapter 12 Chain of Responsibility

12

• The book gives a prototypical example of the kind of relationship where calls are passed

• This example is when objects are related in a tree structure

• Calls to one object can be passed “up the tree” until they can be satisfied

Page 13: Chapter 12 Chain of Responsibility

13

• Notice that this is a responsibility pattern• You decrease the coupling between the client

and the base objects it uses• In other words, the client can merrily call the

desired method on (potentially many different) objects, not knowing how the functionality is achieved, as usual

Page 14: Chapter 12 Chain of Responsibility

14

• However, you offload responsibility from one base object that is called to another which provides the functionality

• This means that there is increased coupling among the base objects in the design

Page 15: Chapter 12 Chain of Responsibility

15

Book Definition of Pattern

• Book definition:• The intent of the Chain of Responsibility

pattern is to avoid coupling the sender of a request to its receiver, by giving more than one object a chance to handle the request.

Page 16: Chapter 12 Chain of Responsibility

16

More Preliminary Ideas

• Think about how inheritance works again• A subclass either inherits a method or

overrides it• If it overrides it, it has the ability to buck the

call upwards using super

Page 17: Chapter 12 Chain of Responsibility

17

• Now consider the chapter on composites again

• The whole idea there was that a set of objects ended up in a tree-like, or hierarchical relationship

• However, this was not an is-a or is-a-kind-of inheritance relationship

• It was a has-a relationship

Page 18: Chapter 12 Chain of Responsibility

18

• Recall that in UML the composition symbol, the diamond, represents a has-a relationship

• Using that terminology, what is under consideration now is a composition relationship

• The Chain of Responsibility design pattern is intended for use in this kind of situation

Page 19: Chapter 12 Chain of Responsibility

19

• In the original composite pattern, an instance of the composite class “has an” instance of the component class

• This means that the parent of the has-a relationships knows about its children

• In the scenario under consideration it’s necessary to buck things up the hierarchy

• This will require including a link, or reference, so that a child in a has-a relationship knows who its parent is

Page 20: Chapter 12 Chain of Responsibility

20

• One last general remark• Even though the name of the pattern is Chain

of Responsibility, all of the concrete examples will be of the form “Tree of Responsibility”

• …Of course, there is no such thing as a tree of responsibility

Page 21: Chapter 12 Chain of Responsibility

21

• Random association comment mode on:• Tree of life?• Chaingang of love?• Web of deceit?

Page 22: Chapter 12 Chain of Responsibility

22

• In theory, the internal relationships of a set of classes in a has-a relationships could be of any kind

• As long as there was a discernible logic to the relationships and responsibilities it would be possible to implement some sort of chain of responsibility in the structure

Page 23: Chapter 12 Chain of Responsibility

23

• A linked list or a doubly linked list could have a chain of responsibility, for example

• A call to one object would be bucked to its neighbor until the method returned the desired result

Page 24: Chapter 12 Chain of Responsibility

24

An Ordinary Chain of Responsibility

• The authors begin describing the pattern with a concrete example

• In a factory setting, various machines may have specific engineers assigned to them

• A sufficiently important or complicated machine may have its own engineer

• A simpler machine which is part of a composition of machines may have as its engineer the engineer assigned to the composition of machines that it belongs to

Page 25: Chapter 12 Chain of Responsibility

25

• From the design pattern point of view, the software goal can be described as follows:

• Suppose client code is trying to find the engineer assigned to a given machine

• It is desirable for the client code only to have to make one call

Page 26: Chapter 12 Chain of Responsibility

26

• This is the undesirable scenario:• The client code makes the call• The call returns null, or some other value

indicating lack of success• The client then has to make a call to obtain a

reference to the parent• The client then makes the call on the parent• This could continue an arbitrary number of times,

from parent to grandparent and so on

Page 27: Chapter 12 Chain of Responsibility

27

• In other words, you don’t want the client code cluttered up in this way:

• Make a call to get the engineer of an object• If not successful, make a call to get the parent of

an object• Make a call to get the engineer of the parent• If not successful, make a call to get the parent of

the parent• …

Page 28: Chapter 12 Chain of Responsibility

28

• Suppose that the method for getting the responsible engineer is getResponsible()

• You’d like the composition hierarchy to be structured so that the client code simply has to call getResponsible() on an object

• If the object doesn’t have an engineer directly assigned to it, in the implementation of getResponsible() there is a call to getResponsible() on the object’s parent

Page 29: Chapter 12 Chain of Responsibility

29

• Notice that this is reminiscent of recursion • The respective calls to getResponsible() work their way

up a has-a tree until they reach the root• The root case will be considered later• The book’s UML diagram of the composite under

consideration is shown on the next overhead• Note that getResponsible() returns an instance of the

Engineer class• Note also that getResponsible() is inherited, not

overridden

Page 30: Chapter 12 Chain of Responsibility

30

Page 31: Chapter 12 Chain of Responsibility

31

• Note that the MachineComponent class has a responsible:Engineer instance variable as well as a getResponsible():Engineer method

• There is no requirement that the responsible instance variable not be null

• In the code, if(responsible == null) would be the trigger for calling getResponsible() on the parent object

Page 32: Chapter 12 Chain of Responsibility

32

• Challenge 2.1• “Point out two weaknesses of the design

shown in Figure 12.1.”• Comment mode on: They point out more

than two.• Take your pick…

Page 33: Chapter 12 Chain of Responsibility

33

• Solution 12.1• “Some potential disadvantages of the Chain of

Responsibility design that Oozinoz uses for finding a machine’s responsible engineer include the following.

Page 34: Chapter 12 Chain of Responsibility

34

• “1. We haven’t specified how the chain will be set up so that machines know their parent.

• In practice, it may be difficult to ensure that parents are never null.”

Page 35: Chapter 12 Chain of Responsibility

35

• Comment mode on:• Note that this is talking about null parents, not

null responsible engineers. • What they’re saying is related to what happened

with the composite examples: • How do you know that the (tree) structure is set

up correctly? • How do you know you have at least one parent as

well as no more than one parent?

Page 36: Chapter 12 Chain of Responsibility

36

• “2. It is conceivable that the search for a parent could enter an infinite loop, depending on how the parents are set up.”

• [This is the same situation as in the previous point

• The tree structure could be incorrectly set up and contain a cycle, for example.]

Page 37: Chapter 12 Chain of Responsibility

37

• “3. Not all objects have all the behaviors implied by these new methods.

• (For example, the top level item has no parent.)”• [This is related to the previous points• How do you know you have a correctly

structured tree?• Also, how do you deal with the fact that the root

node of a tree is special?]

Page 38: Chapter 12 Chain of Responsibility

38

• “4. The present design is light on details regarding how the system knows which engineers are currently in the factory and available.

• It’s not clear how “real time” this responsibility needs to be.”

• [End of challenge solution.]

Page 39: Chapter 12 Chain of Responsibility

39

• The book says the following:• This pattern helps simplify code when it’s not obvious

which object in a group should handle a request.• Stated more accurately, this pattern helps when there

is an internal logic to which object might handle a request

• If there is an internal pattern, using the design pattern might make client code considerably easier to write

Page 40: Chapter 12 Chain of Responsibility

40

Refactoring to Chain of Responsibility

• One sign that code might benefit from the chain of responsibility design pattern is when client code makes “probing” calls

• In the absence of the design pattern, the assumption would be that every class implements the desired method

• Depending on the situation, the method might return null when called on some objects

Page 41: Chapter 12 Chain of Responsibility

41

• The book restates the idea of the pattern in this way:

• To implement the design pattern, make sure that every class in question implements the method—with a chaining strategy for those that can’t return a value

• In other words, you want to write code so that you don’t have situations where a call to the method simply returns null

Page 42: Chapter 12 Chain of Responsibility

42

Tools and Tool Carts

• The book wants to introduce an example where some kinds of objects can never satisfy a call to getResponsible() without passing the call on to their parents

• The book illustrates this extension by adding tools and tool carts to the design scenario

Page 43: Chapter 12 Chain of Responsibility

43

• Tool carts have engineers assigned to them, like machines do

• Individual tools do not have engineers assigned to them

• The engineer assigned to a tool is always the engineer assigned to the cart that the tool belongs to

Page 44: Chapter 12 Chain of Responsibility

44

• Tools and tool carts are a somewhat unfortunate addition to the example

• You might assume that a tool cart was treated as a “composite machine” and a tool was a “leaf machine”

• They are not integrated into the composite hierarchy in this way

Page 45: Chapter 12 Chain of Responsibility

45

The Visualization Interface

• The tool and tool cart ideas are integrated with the machine composite hierarchy by introducing a new interface into the example

• The elements of a graphical application would have a visual representation

• These visual elements might include machine composites, tool carts, and tools

Page 46: Chapter 12 Chain of Responsibility

46

• Client code would make use of objects that implemented a VisualizationItem interface

• Both machine related items and tool related items implement this interface

• The UML diagram on the following overhead shows the scenario

Page 47: Chapter 12 Chain of Responsibility

47

Page 48: Chapter 12 Chain of Responsibility

48

• This UML diagram is for an un-refactored design

• MachineComponent, ToolCart, and Tool all implement the VisualizationItem interface

• getResponsible() is not declared in the VisualizationItem interface, so not all classes that implement it would need getResponsible()

Page 49: Chapter 12 Chain of Responsibility

49

• MachineComponent has getResponsible() based on previous discussion

• Machine and MachineComposite have it by inheritance

• ToolCart is also shown as having getResponsible()

• The Tool class doesn’t have that method

Page 50: Chapter 12 Chain of Responsibility

50

The AmbitiousMenu

• In client code, it will be necessary to check which kind of object you’re working with before calling getResponsible()

• Code will be shown for an AmbitiousMenu class which calls getResponsible() on various kinds of visualization objects, like machines and tools

• This has to be done with a series of if statements, “probing” code, because not all of the classes implement getResponsible()

Page 51: Chapter 12 Chain of Responsibility

51

• The existence of probing code suggests that refactoring would help

• It is not so much that AmbitiousMenu should be rewritten

• It’s that the machine/tool code should be rewritten• If this is done according to the Chain of Responsibility

design pattern, the code for AmbitiousMenu will be greatly simplified

• The code of AmbitiousMenu is shown on the following overheads

Page 52: Chapter 12 Chain of Responsibility

52

• public class AmbitiousMenu • {• public Engineer getResponsible(VisualizationItem item) • {• if (item instanceof Tool)• {• Tool t = (Tool) item;• return t.getToolCart().getResponsible();• }• if (item instanceof ToolCart) • {• ToolCart tc = (ToolCart) item;• return tc.getResponsible();• }

Page 53: Chapter 12 Chain of Responsibility

53

• if (item instanceof MachineComponent) • {• MachineComponent c = (MachineComponent) item;• if (c.getResponsible() != null) • return c.getResponsible();

• if (c.getParent() != null) • return c.getParent().getResponsible();• }• return null;• }• }

Page 54: Chapter 12 Chain of Responsibility

54

• In this example an instance of the menu would be the client

• The method getResponsible() would be called on a menu, passing in a machine, cart, or tool

• Inside that method, getResponsible() is called on VisualizationItem objects

Page 55: Chapter 12 Chain of Responsibility

55

• Challenge 12.2• “Redraw the diagram in Figure 12.2, moving

the getResponsible() method to VisualizationItem and adding this behavior to Tool.”

Page 56: Chapter 12 Chain of Responsibility

56

• Comment mode on:• This is not hard• If each kind of item will have a getResponsible()

method, it makes sense for it to be in the common interface

• The relationships in the implementation will be critical

• Figure 12.2 is shown again on the following overhead

Page 57: Chapter 12 Chain of Responsibility

57

Page 58: Chapter 12 Chain of Responsibility

58

• Solution 12.2• “Your diagram should look similar to Figure

B.15.”

Page 59: Chapter 12 Chain of Responsibility

59

Page 60: Chapter 12 Chain of Responsibility

60

• Comment mode on:• Actually, the book answer is deceptively

simple.• It shows the interface, Tool, ToolCart, and

MachineComponent with a getResponsible() method

• It happens to eliminate the Machine and MachineComposite classes from the diagram

Page 61: Chapter 12 Chain of Responsibility

61

• Comment mode continued:• Even though those classes don’t appear in this

solution diagram, they’re still part of the design• They already have the getResponsble() method

in them and they don’t have to be shown again• As subclasses of MachineComponent they

inherit the getResponsible() method• Eventually, they may also override it

Page 62: Chapter 12 Chain of Responsibility

62

The Benefits and Revised Menu Code

• The (beneficial) results of the design change reflected in the new UML diagram:

• The menu code becomes much simpler as a result of this design change

• The implementation of the getResponsible() method in each class is also easy

• The menu code is given on the next overhead

Page 63: Chapter 12 Chain of Responsibility

63

• public class AmbitiousMenu2• {• public Engineer getResponsible(VisualizationItem item)• {• return item.getResponsible();• }• }

Page 64: Chapter 12 Chain of Responsibility

64

• Challenge 12.3• “Write the code for the getResponsible()

method for:• A. MachineComponent• B. Tool• C. ToolCart”

Page 65: Chapter 12 Chain of Responsibility

65

• Solution 12.3• “A. A MachineComponent object may have

an explicitly assigned responsible person.• If it doesn’t, it passes the request to its

parent.”• [The code is shown on the following

overhead.]

Page 66: Chapter 12 Chain of Responsibility

66

• public Engineer getResponsible()• {• if(responsible != null)• return responsible;• if(parent != null)• return parent.getResponsible();• return null;• }

Page 67: Chapter 12 Chain of Responsibility

67

• “B. The code for Tool.Responsible reflects the statement that “tools are always assigned to tool carts”:”

• public Engineer getResponsible()• {• return toolCart.getResponsible();• }

Page 68: Chapter 12 Chain of Responsibility

68

• “C. The ToolCart code reflects the statement that “tool carts have a responsible engineer”:”

• public Engineer getResponsible()• {• return responsible;• }

Page 69: Chapter 12 Chain of Responsibility

69

Setting Up Parents

• The getResponsible() code for MachineComponent had an if statement to handle the case of a null parent

• A parent instance variable exists in this class and has been shown in the UML diagrams

• However, no code has been given so far that gave objects parents

• Likewise, no code has been given that required objects to have parents

Page 70: Chapter 12 Chain of Responsibility

70

• Construction time would be a good time to specify an object’s parent

• If parents can’t be null, it might also be helpful to throw an exception if that requirement isn’t met

• The idea of a constructor throwing an exception was discussed as a side note in Chapter 8 on the Singleton design pattern

• You also have to deal with the special case of the root node, which doesn’t have a parent

Page 71: Chapter 12 Chain of Responsibility

71

Anchoring a Chain

• In reference to trees, anchoring a chain refers to making sure a specific root is set up for the tree

• The book suggests that a special class, MachineRoot, be created as a subclass of MachineComposite

• The basic idea is shown on the following overhead

Page 72: Chapter 12 Chain of Responsibility

72

Page 73: Chapter 12 Chain of Responsibility

73

• There are several alternatives to this, which can be rejected for logical reasons in general

• Also, when you consider how constructors will be set up to make everything work, you realize that this is the best solution

• The poor alternatives are considered next

Page 74: Chapter 12 Chain of Responsibility

74

• The first alternative that doesn’t work is that the root would be an instance of the component class

• This is out of the question because the component class is abstract

Page 75: Chapter 12 Chain of Responsibility

75

• The second alternative that doesn’t work is that the root class is a subclass of leaf

• This obviously doesn’t work• You would be limited to trees consisting of

one node, the root, which is a leaf

Page 76: Chapter 12 Chain of Responsibility

76

• This is the third alternative that doesn’t work:• If the first two don’t work, you might think that

the root class should be a third subclass of component

• But in the interesting case, where the tree is more than just a single root node, that means that the root is a kind of composite

• Therefore, it should be implemented it as a subclass of composite

Page 77: Chapter 12 Chain of Responsibility

77

• This solution does rule out a single leaf as the root, which may be a minor logical limitation

• The root has to be a composite, but that composite doesn’t have to have anything added to it

• So you still allow for trees that consist of one node

• The root simply has to be composite

Page 78: Chapter 12 Chain of Responsibility

78

getResponsible() with a Root

• What effect does a root have on the implementation of getResponsible() in the machine composite hierarchy?

• A MachineComponent will have a responsible engineer if the following three requirements are met in setting up the code:

• 1. The constructor for MachineRoot will require a responsible Engineer object

• It can be set up to throw an exception if one isn’t provided

Page 79: Chapter 12 Chain of Responsibility

79

• 2. The constructor(s) for MachineComponent will require a parent object that is of type MachineComponent.

• Logically you might think that parents ought to be composites instead of components

• However, the superclass MachineComponent doesn’t “know” its subclass MachineComposite

• Therefore, if you are going to define parenthood in the component class, then a parent has to be of the type component

Page 80: Chapter 12 Chain of Responsibility

80

• The idea is that you define construction in component

• Then the constructors in Machine and MachineComposite can be implemented by calling super

• It seems that you could also define construction in the subclasses and use MachineComposite as the specific parent type if you wanted to

Page 81: Chapter 12 Chain of Responsibility

81

• 3. Only MachineRoot uses null as the value for its parent.

• You might try to enforce this on the client side, by only passing in null as the parent construction parameter for the root

• Alternatively, you could set up an exception throwing mechanism to disallow null as the parent for non-root nodes

Page 82: Chapter 12 Chain of Responsibility

82

• Challenge 12.4• “Fill in the constructors in Figure 12.3 to support

a design that ensures that every MachineComponent object has a responsible engineer.”

• Comment mode on:• Keep in mind that this will involve both parent

and responsible engineer construction parameters

Page 83: Chapter 12 Chain of Responsibility

83

Page 84: Chapter 12 Chain of Responsibility

84

• Solution 12.4• “Your solution should look something like Figure

B.16.• Your constructors should allow Machine and

MachineComposite objects to be instantiated with or without an assigned engineer.

• Whenever a MachineComponent object does not have an assigned engineer, it can get a responsible engineer from its parent.”

Page 85: Chapter 12 Chain of Responsibility

85

Page 86: Chapter 12 Chain of Responsibility

86

Implementing getResponsible() in MachineComponent

• Anchoring a chain of responsibility improves the way the object-oriented design models reality

• The fundamental thing that’s been accomplished is this:

• Every node except the root has a parent• Not all nodes have a responsible engineer, but

the root has to have one

Page 87: Chapter 12 Chain of Responsibility

87

• Anchoring the chain in this way simplifies the code

• The getResponsible() method of the MachineComponent class can be implemented as shown on the following overhead

• This can be inherited by every class under it in the hierarchy

Page 88: Chapter 12 Chain of Responsibility

88

• public Engineer getResponsible()• {• if(responsible != null)• return responsible;• return parent.getResponsible();• }

Page 89: Chapter 12 Chain of Responsibility

89

Chain of Responsibility without Composite

• The Chain of Responsibility design pattern relies on a structure, like a tree, in the problem domain

• That structure provides an order for searching for the object responsible for providing some service

• It was convenient to illustrate the idea using composition, which arose as an earlier pattern

• However, the chain of responsibility doesn’t have to be based on composition (a tree-like structure)

Page 90: Chapter 12 Chain of Responsibility

90

• Challenge 12.5• “Cite an example in which the Chain of

Responsibility pattern might occur where the chained objects do not form a composite.”

Page 91: Chapter 12 Chain of Responsibility

91

• Solution 12.5• “Chain of Responsibility might apply to objects that do not

form a composite when• 1. A chain of on-call engineers follows a standard rotation.• If the primary on-call engineer does not answer a

production support page in a specific amount of time, the notification system pages the next engineer in the chain.

• 2. Users enter information, such as the date of an event; a chain of parsers can take turns trying to decode the user’s text.”

Page 92: Chapter 12 Chain of Responsibility

92

Another Example

• A translation of this general idea into the cup and seed realm is given in the UML diagram on the following overhead

• A class is included that explicitly roots the hierarchy

• Also, just like machines have responsible engineers, cups have owners

Page 93: Chapter 12 Chain of Responsibility

93

Page 94: Chapter 12 Chain of Responsibility

94

Lasater’s UML

• Lasater’s UML diagram is given on the following overhead

• It also translates the idea in a straightforward way• It includes a client• It also makes it clear that an owner, a reponsible

party, what have you, is not intrinsic to the pattern• What is intrinsic is the implementation of a set of

methods

Page 95: Chapter 12 Chain of Responsibility

95

Page 96: Chapter 12 Chain of Responsibility

96

Summary

• The Chain of Responsibility pattern relieves a client from having to know which objects in a collection support which behavior

• Note that in order to support this, concretely every object in a collection has a method of the desired name

• Some of the objects may pass a call to the method to another object in the collection

Page 97: Chapter 12 Chain of Responsibility

97

• This decouples the client from the objects• It also means that the objects are now more

tightly coupled, since they pass requests on to other objects in the collection

Page 98: Chapter 12 Chain of Responsibility

98

• The main example given of a chain of responsibility was behavior in a composition

• In other words, there’s a containment hierarchy where calls can be bucked up the hierarchy

• The book also mentions parsing as an application domain, but without an example or much clear explanation

Page 99: Chapter 12 Chain of Responsibility

99

• When applied in an appropriate problem domain, the Chain of Responsibility design pattern can accomplish the following:

• Simpler code in the client• Also, simple code in the collection of objects

which neatly passes around calls in a manner reflecting the underlying structure of the containment hierarchy (or other problem domain)

Page 100: Chapter 12 Chain of Responsibility

100

The End


Recommended