Professional Documents
Culture Documents
Design Patterns Java 1
Design Patterns Java 1
In Java
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Course overview
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Software cost
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Software quality
• Goals:
Correctness
Robustness
Extendibility
Reusability
Compatibility
Efficiency
Portability
Ease of use
Functionality
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
What is software design?
• Implement software solutions
• Transform user requirements
• Software design levels:
– Architectural Design
• Highest abstract version of a system
• Many components interacting with each other
– High-level Design
• Identifies sub systems and modules
• Interaction between modules
– Low-level Design
• Logical structure of each module
• Interfaces to communicate with other modules
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
What is good design?
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Modularity
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Modularity
• Criteria :
Decomposability
• Are larger components decomposed into smaller components?
Composability
• Are larger components composed from smaller components?
Understandability
• Are components separately understandable?
Continuity
• Do small changes to the specification affect a localized and limited
number of components?
Protection
• Are the effects of run-time abnormalities confined to a small
number of related components?
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
1. Decomposability
• Decompose problem into smaller sub-
problems that can be solved separately
Goal: Division of Labor
• keep dependencies explicit and minimal
Example: Top-Down Design
Counter-example: Initialization Module
initialize everything for everybody
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
2. Composability
• Freely combine modules to produce new
systems
Reusability in different environments ->
components
Example: Math libraries; UNIX command & pipes
Counter-example: use of pre-processors
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
3. Understandability
• Individual modules understandable by human
reader
Counter-example: Sequential Dependencies (A | B
| C)
• contextual significance of modules
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
4. Continuity
• Small change in requirements results in:
– changes in only a few modules does not affect the
architecture
– Example: Symbolic Constants
– Counter-Example: static arrays
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
5. Protection
• Effects of an abnormal run-time condition is
confined to a few modules
Example: Validating input at source
Counter-example: Undisciplined exceptions
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Five Rules of Modularity
• Direct Mapping
consistent relation between problem model and solution
structure
• Few Interfaces
Every component should communicate with as few others as
possible
• Small Interfaces
If any two components communicate at all, they should
exchange as little information as possible
• Explicit Interfaces
Whenever two components A and B communicate, this must be
obvious from the text of A or B or both
• Information Hiding
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
1. Direct Mapping
• Keep the structure of the solution compatible
with the structure of the modeled problem
domain
clear mapping (correspondence) between the two
• Impact on:
– Continuity
• easier to assess and limit the impact of change
– Decomposability
• decomposition in the problem domain model is a good
starting point for the decomposition of the software
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
2. Few Interfaces
• Every module should communicate with as
few others as possible
rather n-1 than n(n-1)/ 2
Continuity, Protection, Understandability,
Composability
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
3. Small Interfaces
• If two modules communicate, they should
exchange as little information as possible
limited "bandwidth" of communication
Continuity and Protection
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
4. Explicit Interfaces
• Whenever two modules A and B
communicate, this must be obvious from the
text of A or B or both.
Decomposability and Composability
Continuity, Understandability
• The issue of indirect coupling
data sharing
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
5. Information Hiding
• Motivation: design decisions that are subject
to change should be hidden behind abstract
interfaces, i.e. components
Components should communicate only through
well-defined interfaces
Each component is specified by as little
information as possible
Continuity: If internal details change, client
components should be minimally affected
• not even recompiling or linking
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Abstraction vs. Information Hiding
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
OO Design Heuristics
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
What’s next?
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Classes and Objects
• Heuristics
– All data should be hidden within its class
• When a developer says
– “I need to make this piece of data public because…”
• They should ask themselves
– “What is it that I’m trying to do with the data and why doesn’t
the class perform that operation for me?”
– Users of a class must be dependent on its public
interface, but a class should not be dependent on
its users
• Why?
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Grade Calculator
• GradeCalculator class with 3 methods:
– readGradeInput
– computeGrades
– showGradeOutput
• if the 3 methods are not called in exactly this order,
GradeCalculator code crashes! Solutions?
• computeGrades or showGradeOutput call
their preceding methods if that hasn't been
done already
• combine these methods into only one public
method that calls all 3 in order
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Dependencies
• Class dependencies
• Interface Dependencies
• Method/Field dependencies
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Classes and Objects
• Heuristics
Minimize the number of messages in the protocol
of a class
• The problem with large public interfaces is that you can
never find what you are looking for…smaller public
interfaces make a class easier to understand and
modify
• Example: LinkedList has too many methods, hard to find
a method for merge operation
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Classes and Objects
• Heuristics
Do not put implementation details such as common code
“helper” functions into the public interface of a class
• Users of a class do not want to see operations in the public
interface that they are not supposed to use
• LinkedList class has add and remove methods that both
advance the list to the proper linked node to add/remove, then
performs the operation. How should we design this?
• use a common helper named getNodeAt(int index)
that advances the list to the proper node and returns
that node
• make the helper getNodeAt a private method so
client code does not see it and cannot call it
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Classes and Objects
• Heuristics
A class should capture one and only one key abstraction
• (bad) example: PokerGame class that holds all the players, holds an
array representing the card deck, stores all bets and money, does the
logic for each betting round...
Keep related data and behavior in one place
• example: Should a poker game's Player class remember whether
that player is in the game, what that player's current bet is, etc? Or
should the Game class remember who is in the game, the Bet class
remember every player's current bets, ...?
Spin off non-related information into another class
• Similar to the “Extract Class” refactoring pattern
Most of the methods defined on a class should be using most of
the data members most of the time
• All of these heuristics deal with class cohesion
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Action Oriented Topology
• Procedural topologies break an application down
according to function which then share data
structures
• It is easy to see which functions access which
data structures
• It is difficult to go the other way, to see which
data structures are used by which functions
• How to apply it correctly
– Put the data in the same file with the functions on it
– So why not make it a class
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Object Oriented Topology
• Collections of decentralized data
• Well defined interfaces
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Action vs. Object-Oriented
• There are two typical problems that arise
when developers familiar with procedural
techniques try to create an OO design
The God Class
• A single class drives the application, all other classes
are data holders
Proliferation of Classes
• When object-oriented design leads us to design a
system that has too many classes that are too small in
size and scope, making the system hard to use, debug,
and maintain
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
God Class
• Avoidance:
– Distribute system intelligence horizontally as uniformly as
possible, that is, the top-level classes in a design should
share the work uniformly
• Anticipate:
– Be very suspicious of a class whose name contains
“Driver”, “Manager”, “System”, or “Subsystem”
– Beware of classes that have many accessor methods
defined in their public interface. Having many implies that
related data and behavior are not being kept in one place
– Beware of classes whose methods operate on a proper
subset of the data members of a class. God classes often
exhibit this behavior
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
God Class Example
• A heat flow regulator needs to decide when to activate a furnace to keep a
room at a certain temperature
• Bad example
• Good example
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Proliferation of Classes
• Bad example
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Proliferation of Classes
• Good example
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Classes and Objects
• Heuristics
Minimize the number of classes with which another class
collaborates
• Look for situations where one class communicates with a group of
classes
– Ask if its possible to replace the group with a class that contains the
group
Related Heuristics
• Minimize the number of message sends between a class and its
collaborator
• Minimize the amount of collaboration between a class and its
collaborator, that is, the number of different messages sent
• Minimize fanout in a class, that is, the product of the number of
messages defined by the class and the messages they send
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Classes and Objects
• Heuristic
If a class contains objects of another class, then the
containing class should be sending messages to the
contained objects
• that is a containment relationship should always imply a uses
relationship
• Related
Classes should not contain more objects than a
developer can fit in short-term memory.
A class must know what it contains, but it should not
know its container (do not depend on your users)
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Inheritance
• Heuristics
Inheritance should be used only to model a
specialization hierarchy
• Containment is black-box
• Inheritance is white-box
Derived classes must have knowledge of their
base class by definition, but base classes should
not know anything about their derived classes
All data in a base class should be private; do not
use protected data
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Inheritance
• Heuristics
In theory, inheritance hierarchies should be
deep—the deeper, the better
• In practice, inheritance hierarchies should be no deeper
than an average person can keep in short-term
memory.
All abstract classes must be base classes
All base classes should be abstract classes
Factor the commonality of data, behavior, and/or
interface as high as possible in a class hierarchy
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Inheritance
• A Second Heuristic
Whenever there is inheritance in an object-oriented
design, ask yourself two questions:
1) Am I a special type of the thing from which I’m
inheriting? 2) Is the thing from which I’m inheriting
part of me?
• A yes to 1) and no to 2) implies the need for
inheritance; A no to 1) and a yes to 2) implies the
need for composition
Is an airplane a special type of fuselage? No
Is a fuselage part of an airplane? Yes
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Multiple Inheritance
• Riel does not advocate the use of multiple
inheritance (its too easy to misuse it). As such,
his first heuristic is
If you have an example of multiple inheritance in
your design, assume you have made a mistake and
prove otherwise!
• Most common mistake
Using multiple inheritance in place of containment
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Effective Java by Bloch
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Examples from Bloch
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Static Factory Methods
• Typically an object is obtained from a class
constructor
public Boolean(boolean value) {
_value = value;
}
• but an alternative is to use a static factory
method
public static Boolean valueOf(boolean b) {
return (b ? Boolean.TRUE : Boolean.FALSE);
}
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Static Factory Methods
• Benefits
static factory methods have names
• two constructors with same signatures not allowed
– factory methods can simply use different names
static factory methods do not have to create a new
object each time they are invoked
• consider implementation of the flyweight pattern, no matter
how many times the factory method was invoked, only a
maximum of 26 objects could be created
static factory methods can return an object of any
subtype of their return type; constructors cannot
• this method allows frameworks to return objects whose
classes are private; like Java’s ListIterator
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Static Factory Methods
• Benefits:
Static factory methods reduce the verbosity of
creating parameterized type instances
Instead of:
Use:
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Static Factory Methods
• Disadvantages
If a class provides only static factory methods, and
makes its constructor private, then the class
cannot be subclassed
static factory methods are not readily
distinguishable from other static methods
• whereas constructors “stand out” since their name
matches the name of the class
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Builder
• Bad example
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Builder
• Better example:
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Builder
• Best example
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Immutability
• Consider creating immutable classes
An immutable class is simply a class whose instances
cannot be modified
• Why?
Immutable objects are simple
• they can be in only one state
Immutable objects are thread-safe
• since they never change value, multiple threads can access
them without synchronization!
Immutable objects can be shared freely
• one object can’t change the value of an immutable object
unexpectedly, because the value cannot change at all!
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Immutability
• To make a class immutable
Do not provide mutator methods
• e.g. setName(String name)
Ensure that methods cannot be overridden
• make constructor private, add factory method
Make all fields final (e.g. const)
Make all fields private
• you should do this anyway
Ensure exclusive access to any mutable components
• Do not store a pointer to a mutable object provided from
the outside world, and do not return a pointer to any of your
mutable objects; perform defensive copies instead
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Immutability
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Immutability
• Disadvantage
Immutable objects require a separate object for each
distinct value
• Turning lemons into lemonade
use a static factory method to manage a pool of
objects for your immutable class
if two objects with the same value are requested,
return the same object each time
• this lets the class ensure that a==b is equivalent to
a.equals(b)
– e.g. a logically equals b, only when a and b point to the same
object)
– the latter is typically much faster at run-time
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Favor Composition
• Inappropriate use of inheritance leads to
fragile software
Why? Because inheritance breaks encapsulation;
subclasses can see and access the internals of
their parents (unless protected by “private”)
• thus, a subclass may “break” due to a change in one of
its parents, even when the source code of the subclass
was not modified
– mainly because a subclass may have depended on the
implementation details of its parents
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Favor Composition
• Bad example
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Favor Composition
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Favor Composition
IHashSet s = new IHashSet()
s. addAll(
Arrays.asList(
new String[] {“Snap”, “Crackle”, “Pop”}));
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Favor Composition
• Rather than subclass HashSet, lets wrap it,
furthermore, since HashSet implements the Set
interface, lets create a Wrapper that can handle
any class that implements the Set interface
Our new class ISet will take a reference to an existing
Set, forward all Set related calls to it, and add our
instrumentation to the add and addAll operations
• now since we are delegating to a composed object, rather
than overriding inherited methods, polymorphism no longer
applies; HashSet’s addAll method is free to use HashSet’s
add method (or not) and our code still works
See code next slide
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Favor Composition
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Favor Composition
• Now ISet can handle any class that implements
the Set interface, not just HashSet
Set s1 = new ISet(new TreeSet(list));
Set s2 = new ISet(new HashSet(20));
• ISet is an instance of the Decorator pattern, it
wraps other Set classes
• Disadvantages
– Hard to use in callback frameworks
• because wrapped objects do not know they are wrapped
– Writing forwarding methods is tedious
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Prohibit Inheritance
• How do you design and document a class for
inheritance
if not done properly, problems can arise, as we saw in the
last example
• we did not know that HashSet.addAll invoked HashSet.add
• First, a class must document precisely the effects of
overriding any method
including documenting its self-use of its own methods;
which methods and in what order
• Now, wait a minute! Does this violate the dictum that
good documentation should describe “what” a method
does not “how” it does it? Yes! Why? Because
inheritance breaks encapsulation!
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Inheritance
• Second, to provide hooks to subclasses,
protected methods must be provided
choose with care if your class is used by external
clients
• you are committing to these methods (and your self-use
documentation from the previous slide) forever!
• Third, constructors must not invoke overridable
methods
since superclass constructors are invoked before
subclass constructors, use of an overridable method
may cause a subclass method to be invoked before
the subclass constructor has been invoked!
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Inheritance
• What is being printed on the screen?
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Inheritance
• By now it should be apparent that designing a
class for inheritance places substantial limitations
on the class
hence this is not a decision to be taken lightly
• The best solution to this problem is to prohibit
subclassing in classes that are not designed and
documented to be safely subclassed
Easiest way to do this is to make the constructors
private and provide only public static factory methods
• If this is not an option, consider designing your
class to eliminate self-use
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Principles and Rules of OOD
• Open Close Principle
• Dependency Inversion Principle
• Liskov Substitution Principle
• Single Responsibility Principle
• Interface Segregation Principle
• Law of Demeter
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Open Close Principle
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Open Close Principle
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Open Close Principle
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Open Close Principle
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Dependency Inversion Principle
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Dependency Inversion Principle
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Design to an Interface
• Abstract classes/interfaces:
tend to change less frequently
abstractions are ‘hinge points’ where it is easier to
extend/modify
shouldn’t have to modify classes/interfaces that represent the
abstraction (OCP)
• Exceptions
Some classes are very unlikely to change (example: String class)
In cases like this can use concrete class directly
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
DIP Bad Example
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
DIP Good Example
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Liskov Substitution Principle
• Any code which can legally call another class’s
methods must be able to substitute any
subclass of that class without modification
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Square IS-A Rectangle?
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
LSP
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Single Responsibility Principle
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Single Responsibility Principle
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Interface Segregation Principle
• Consequence:
impact of changes to one interface aren’t as big if
interface is smaller
interface pollution
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Law of Demeter
• Weak Form
Inside of a method M of a class C, data can be accessed in and
messages can be sent to only the following objects:
this and super
data members of class C
parameters of the method M
object created within M
by calling directly a constructor
by calling a method that creates the object
global variables
• Strong Form:
In addition to the Weak Form, you are not allowed to access directly
inherited members
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Law of Demeter
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Law of Demeter
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania
To be Checked
• Class abstraction
• Functional requirements
• UML
• Coding style
Information Type: Working Standard, Disclosure Range: , Information Owner: iulia.jianu, NTT DATA Romania