46
GRASP Patterns Best Practices for Object- Oriented Software Design

GRASP Patterns Best Practices for Object-Oriented Software Design

Embed Size (px)

Citation preview

Page 1: GRASP Patterns Best Practices for Object-Oriented Software Design

GRASP Patterns

Best Practices for Object-Oriented Software Design

Page 2: GRASP Patterns Best Practices for Object-Oriented Software Design

GRASP Patterns GRASP: Generalized Responsibility

Assignment Software Patterns GRASP patterns are really more accurately

described as best practices GRASP patterns outline best practices which can

be employed in any object-oriented design These best practices, if used properly, will lead

to maintainable, reusable, understandable, and easy to develop software

Page 3: GRASP Patterns Best Practices for Object-Oriented Software Design

GRASP Patterns GRASP patterns describe how to assign

responsibilities to classes Warning: Grasps tend to be vague

Responsibilities is “a contract or obligation of a classifier” (UML definition) Responsibilities can include behaviour, data

storage, object creation and more They often fall into two categories:

Doing Knowing

Page 4: GRASP Patterns Best Practices for Object-Oriented Software Design

1. Information Expert Assign a responsibility (such as behaviour)

to the information expert An information expert is the class that has (or

has direct access to) the information necessary to fulfill the responsibility

e.g. A responsibility such as handling a deposit (i.e. increase balance) should be assigned to the Account class This is because Account contains the account

balance as one of its attributes

Page 5: GRASP Patterns Best Practices for Object-Oriented Software Design

1. Information Expert

This GRASP has the effect of having a class with high cohesion Cohesion – the degree to which the

information and responsibilities of a class are related to each other

Cohesion is improved since the information needed for a responsibility is closely related to the responsibility itself

Page 6: GRASP Patterns Best Practices for Object-Oriented Software Design

2. Creator Give a class A the responsibility of creating

instances of another class, B, if: A is an aggregate of B A is a container of B

If no classes apply, then assign the responsibility to a class C if: C records instances of B C closely uses B objects C has the initializing data for B

Page 7: GRASP Patterns Best Practices for Object-Oriented Software Design

2. Creator Whenever one class has the responsibility

of creating instances of another class, the two classes are coupled Coupling itself is not wrong, but we want to

eliminate certain types of coupling Coupling of classes is a measure of how

strongly a class is connected to another class Whenever two classes are coupled, one class

becomes dependent upon the other to function correctly

Page 8: GRASP Patterns Best Practices for Object-Oriented Software Design

2. Creator

The Creator GRASP ensures that coupling due to object instantiation only occurs on closely related classes An aggregate or container of a class is

already coupled with that class Thus, assigning the creation

responsibility to the container or aggregate does not introduce more coupling

Page 9: GRASP Patterns Best Practices for Object-Oriented Software Design

2. Creator e.g. Consider an Invoice which has a

number of InvoiceItems on it When a new Invoice is created, we might

wish to add new items to it It makes sense that the Invoice itself

would create instances of InvoiceItem, and subsequently add them to itself

The effect is that no other classes should need to know about InvoiceItems (at least not for this responsibility)

Page 10: GRASP Patterns Best Practices for Object-Oriented Software Design

3. Low Coupling Assign a responsibility so that coupling

remains low This is pretty vague, but it means that we try to

keep low the number of classes to which a class is coupled

Creator is a more specific case of Low Coupling, related to instantiation

A good rule of thumb is: If class A is already coupled with class B, assign a responsibility for B to the class A This is only if it is not appropriate to assign the

responsibility directly to A (otherwise, this would be contrary to Information Expert)

Page 11: GRASP Patterns Best Practices for Object-Oriented Software Design

3. Low Coupling The Low Coupling ‘pattern’ is definitely a

best practice It is a good idea to keep coupling low in a design

The reasons why Low Coupling is important should be obvious: With Low Coupling, changes to a class (A) affect

fewer classes (the classes coupled to A) Thus Low Coupling improves the maintainability

of a software system A low coupled class is also easy to understand,

since it is often simpler and more cohesive

Page 12: GRASP Patterns Best Practices for Object-Oriented Software Design

3. Low Coupling e.g. Suppose the total price for an Invoice

needs to be calculated To achieve this, the costs must be totalled of all

InvoiceItem instances Recall that the Creator GRASP already

recommends that we create instances of InvoiceItem within Invoice Thus, Invoice is already coupled with InvoiceItem The responsibility of total price should be

assigned to Invoice, since coupling will not be increased

Page 13: GRASP Patterns Best Practices for Object-Oriented Software Design

4. High Cohesion

Assign a responsibility so that cohesion remains high Again, this is vague, but it simply means

that we should always try to maintain class cohesion

This is another GRASP that is really more like a best practice, than a pattern

Page 14: GRASP Patterns Best Practices for Object-Oriented Software Design

4. High Cohesion

When about to assign a responsibility, ask yourself the following question: Is this responsibility related to the

other responsibilities of this class? If not, there is likely a need to assign

the responsibility to another class This may prompt you to create a new

class if other responsibilities exist that are similar/related to this one

Page 15: GRASP Patterns Best Practices for Object-Oriented Software Design

4. High Cohesion e.g. Consider a class Order, which

stores data about an order that has been placed in an online store Responsibility: Store the contents of an

Order to secondary storage Should this responsibility be added to

the Order class? Are order details and persistence

management related concepts? No!! They are not even close!

Page 16: GRASP Patterns Best Practices for Object-Oriented Software Design

4. High Cohesion To preserve cohesion, Order should not be

given this responsibility So what class should persist order details?

Several possibilities exist, but here is one: A matching class (e.g. OrderDAO) whose

responsibility is merely to persist Orders Likely OrderDAO will have some persistence

code reuse (from a superclass or some other class or subsystem)

Page 17: GRASP Patterns Best Practices for Object-Oriented Software Design

4. High Cohesion Cohesion is important because incohesive

classes are large and cumbersome Such classes are usually thousands of lines long

These classes are difficult to understand and maintain

It is also highly unlikely that a class with so many unrelated responsibilities will be useful in any other context There is little chance of class reuse

Page 18: GRASP Patterns Best Practices for Object-Oriented Software Design

4. High Cohesion By contrast, cohesive classes represent a

single abstraction and responsibilities related to that abstraction Classes representing a single abstraction could

be reused in any context that requires modeling that abstraction

Class reuse is possible when highly cohesive Cohesive classes are easy to maintain

How do you find a bug related to storing order details to the database? OrderDAO

Cohesive classes are more modular, and thus support teamwork

Page 19: GRASP Patterns Best Practices for Object-Oriented Software Design

Cohesion and Coupling Cohesion and Coupling are two of the most

important concepts in software design However, they are not completely

unrelated: Highly coupled classes often are not cohesive A class that has been assigned many

responsibilities for many external classes is unlikely to represent a single abstraction

Page 20: GRASP Patterns Best Practices for Object-Oriented Software Design

5. Controller Assign the responsibility for receiving and

handling a system event message to a class that is either: Representative of the entire subsystem (e.g. a

Façade Controller) Representative of the entire use case scenario

Do not assign these responsibilities to View classes (windows, dialogs, etc.) A Controller is never a user interface object

Page 21: GRASP Patterns Best Practices for Object-Oriented Software Design

5. Controller

This GRASP is just common sense: When a system event occurs, the class

who has the responsibility of performing a high level function should receive events indicating it should take place

Often a subsystem will have one or more Controller classes, each designed to handle certain responsibilities Controller responsibilities are usually

extremely high-level

Page 22: GRASP Patterns Best Practices for Object-Oriented Software Design

5. Controller e.g. In a bank system, we might have a

Controller that manages all banking transactions (TransactionController) This class would have methods such as:

deposit() withdraw() payBill() transfer()

It makes sense that this class receive the event generated when the teller clicks the ‘Execute Bill Payment’ button on the user interface

Page 23: GRASP Patterns Best Practices for Object-Oriented Software Design

5. Controller Some architectures stress the importance

of view/controller separation e.g. Some variations of the MVC, Layers

For these architectures, the Controller GRASP has to be slightly modified: One user interface might generate different

types of events than another UI To solve this problem, a View class can catch the

event, and generate a high-level event corresponding to the requested behaviour

The Controller would handle this event

Page 24: GRASP Patterns Best Practices for Object-Oriented Software Design

5. Controller The GRASP mentioned that a Controller that

represents an entire subsystem might be called a Façade Controller This is a variation of Façade, where the Façade

receives events (as well as method invocation messages) and forwards them to the correct subsystem component for handling

This is essentially the same concept as the Façade described earlier in the course

Page 25: GRASP Patterns Best Practices for Object-Oriented Software Design

5. Controller Warning: Watch out for bloated Controllers

This can happen if you have one Controller for the entire system, for example

Controllers can receive several events, but they should delegate the responsibility of the corresponding functionality to other classes e.g. The Façade Controller will receive the

event, and send a corresponding message to the correct subsystem component to handle it

One global Controller is usually a bad idea One common practice is to create a

Controller for each use case

Page 26: GRASP Patterns Best Practices for Object-Oriented Software Design

5. Controller View classes generally should not receive

system events The reason for this is that when View classes

receive system events, they must call the corresponding method in another class to handle the behaviour

This couples the View with this other class We’ve already discussed why coupling should

be avoided (especially between modules) Worse yet, the View class actually handling the

behaviour would definitely reduce cohesion The View class would have at least two

purposes: user interface and behaviour

Page 27: GRASP Patterns Best Practices for Object-Oriented Software Design

Coupling Coupling cannot be avoided altogether

Without coupling, all we could create would be isolated and static classes

Our software wouldn’t be able to do anything Reducing coupling should be one of the

factors taken into consideration when assigning responsibilities

Also, specific kinds of coupling should be avoided: Coupling between two classes internal to two

different modules would be a big mistake as the internal details of a module should be hidden

Page 28: GRASP Patterns Best Practices for Object-Oriented Software Design

6. Polymorphism When related behaviours vary by type

(class), assign the responsibility polymorphically to the specialization classes This is basically the purpose of polymorphism, so

it is natural for software developers to understand

This is not much of a pattern, and yet another best practice

Page 29: GRASP Patterns Best Practices for Object-Oriented Software Design

6. Polymorphism e.g. Consider a UML diagram drawing

program If shapes are responsible for drawing

themselves via the draw() method: Obviously, an Actor (stick figure) will draw itself

differently than a UseCase (ellipse) It might make sense in this case to have the

classes themselves handle the drawing by polymorphically overriding the draw() method

An advantage is that new entities (e.g. State) can be easily added without changing the core graphics code

Page 30: GRASP Patterns Best Practices for Object-Oriented Software Design

6. Polymorphism Polymorphism can lead to highly cohesive objects Consider the example where some draw() method

were implemented similarly to this:If (entity.type = “UseCase”) then drawEllipse(…);Else if (entity.type = “Class”) then

drawRectangle(…);…End if This is not highly cohesive, since it combines

unrelated behaviours, and it also strongly couples this object with the shape it draws

Page 31: GRASP Patterns Best Practices for Object-Oriented Software Design

7. Pure Fabrication To support high cohesion and low coupling,

where no appropriate class is present: invent one Even if the class does not represent a problem

domain concept This is a compromise that often has to be

made to preserve cohesion and low coupling Remember: the software is not designed to

simulate the domain, but operate in it The software does not always have to be

identical to the real world

Page 32: GRASP Patterns Best Practices for Object-Oriented Software Design

7. Pure Fabrication Pure Fabrication is a compromise when

faced with a choice between modeling the domain and preserving maintainability and class reusability Software maintainability and reuse are always

more important in business, since they are ways companies can preserve resources

Usually, Pure Fabrication is used when there is no appropriate class to use Usually, it is a good idea to try the other patterns

first to try to find a solution which more closely resembles the domain entities

Page 33: GRASP Patterns Best Practices for Object-Oriented Software Design

7. Pure Fabrication e.g. In the previous example is was

suggested that entities in a UML editor draw themselves However, what if this is a difficult task? What if the drawing facilities vary depending on

what drawing facilities the user has installed? e.g. DirectX, OpenGL, etc.

The real world suggests that the Architect class draw the entities This is because architects draw UML diagrams

Page 34: GRASP Patterns Best Practices for Object-Oriented Software Design

7. Pure Fabrication Architect might be used to store user

preferences, recently created diagrams and projects, etc. This would obviously be a cohesion problem

Pure Fabrication would suggest creating a class whose job is to draw the entities One solution might be to have the Entity

instances associate with an instance of EntityRenderer component

There could be a subclass of EntityRenderer for each subclass of Entity e.g. UseCaseRenderer, ActorRenderer

Page 35: GRASP Patterns Best Practices for Object-Oriented Software Design

7. Pure Fabrication

The difficulty then becomes: how do we associate the entity instances with the right instance of renderer? This is a problem easily solved by the

AbstractFactory pattern, discussed later

Page 36: GRASP Patterns Best Practices for Object-Oriented Software Design

8. Indirection To avoid direct coupling between objects,

assign an intermediate object as a mediator1. Recall that coupling between two classes of

different subsystems can introduce maintenance problems

2. Another possibility is that two classes would be otherwise reusable (in other contexts) except that one has to know of the other Coupling the two objects would reduce the

reuse contexts to where both abstract concepts were relevant together The objects could not be reused separately

Page 37: GRASP Patterns Best Practices for Object-Oriented Software Design

8. Indirection e.g. Consider an application for managing

group work Employee instances might need to be coupled to

Project instances However, potential for reuse of both Employee

and Project is high One solution is to assign a class

(Assignment) to couple the two classes In this case, the class represents an association

class (a class that represents an association)

Page 38: GRASP Patterns Best Practices for Object-Oriented Software Design

8. Indirection The Façade pattern is another example of

Indirection The Façade prevents coupling classes in two

different subsystems Classes in one subsystem communicate

directly with the Façade The Façade communicates with the correct

subsystem component Thus, changes to the structure of the

subsystem will not affect the user of the subsystem, thanks to the Façade

Page 39: GRASP Patterns Best Practices for Object-Oriented Software Design

9. Protected Variations Assign responsibility to create a stable

interface around an unstable or predictably variable subsystem or component If a component changes frequently, the users of

the component will also have to be modified This is especially time consuming if the

component has many users Wrapping the component in a stable

interface means that when variations occur, the wrapper class need only be changed In other words, changes are localized

Page 40: GRASP Patterns Best Practices for Object-Oriented Software Design

9. Protected Variations e.g. Big video game companies make

money by creating a 3D graphics game engine (as well as sound, AI, etc.) These video game companies often produce

many games using the same engine, and release the game on many consoles

This is only possible to this extent using Protected Variations If a game is to be ported to another console, the

wrapper object will have to delegate 3D graphics drawing to different console-level commands

However, the wrapper is simpler to change than the entire game and all of its facets

Page 41: GRASP Patterns Best Practices for Object-Oriented Software Design

9. Protected Variations These video game companies facilitate

additional revenue by making their wrapper modules flexible, so that they can create many games using the same interface Thus, a good practice is to leave out

unnecessary details in wrappers It is often useful to try to preserve the cohesion

(and thus reuse factor) of a subsystem by keeping the interface flexible

Obviously, the Façade pattern is also an example of Protected Variations

Page 42: GRASP Patterns Best Practices for Object-Oriented Software Design

9. Protected Variations One popular pattern using Protected

Variations is the Adapter An Adapter is another pattern, discussed later,

which adapts one interface for another Perhaps an application (e.g. a game) is written

to use a one interface (e.g. OpenGL for 3D graphics)

The application (game) might need to be adapted to also run on other platforms (e.g. DirectX/Direct3D)

An Adapter is intended for this kind of application An OpenGL-to-Direct3D adapter is possible

Page 43: GRASP Patterns Best Practices for Object-Oriented Software Design

9. Protected Variations Among other examples are JDBC and ODBC:

These are packages that allow applications to access databases in a DB-independent way In spite of the fact that databases all use slightly

different methods of communication It is possible due to an implementation of

Protected Variations Users write code to use a generic interface

An adapter converts the generic method calls to DB-specific communications and vice versa

Page 44: GRASP Patterns Best Practices for Object-Oriented Software Design

10. Don’t Talk to Strangers

Do not couple two objects who have no obvious need to communicate This is common sense: do not add

coupling where unnecessary Again, this is a best practice in

software Although common sense, this still must

be considered seriously, as a tendency exists to model all possible relationships

Page 45: GRASP Patterns Best Practices for Object-Oriented Software Design

10. Don’t Talk to Strangers Often the domain objects have relationships

that need not be modeled in the application However, a common tendency is to model those

relationships (with aggregation, for instance) anyway

Model relationships only if they are necessary to complete a use case Ultimately it is not relationships in the domain

that determine if relationships in software should be present

Page 46: GRASP Patterns Best Practices for Object-Oriented Software Design

10. Don’t Talk to Strangers e.g. Consider a convenience store

application Customers are people, as are Employees

Should we model this as inheritance? In this application, customers are likely

anonymous Thus, Employee instances (which likely store

names and phone numbers) do not share any common data or behaviour It makes sense that we not model this

relationship, despite our initial reactions