Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 46

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
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
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
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
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
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
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
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)
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)
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
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
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
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
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!
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
8. Indirection
 To avoid direct coupling between objects,
assign an intermediate object as a
mediator
1. 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
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)
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
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
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
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
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
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
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
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
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

You might also like