You are on page 1of 50

ceCS 575

1
7 Object-oriented Design Principles
1. Favor Composition Over Inheritance
2. Program To An I nterface, Not An I mplementation
3. The Open-Closed Principle: Software Entities Should
Be Open For Extension, Yet Closed For Modification
4. The Liskov Substitution Principle: Functions That
Use References To Base Classes Must Be Able To Use
Objects Of Derived Classes Without Knowing It
5. The Law of Demeter
6. Minimize The Accessibility of Classes and Members
7. The Dependency Inversion Principle
ceCS 575
2
Favor Composition Over Inheritance
Composition - Method of reuse in which new
functionality is obtained by creating an object
composed of other objects
The new functionality is obtained by delegating
functionality to one of the objects being composed
Sometimes called aggregation or containment,
although some authors give special meanings to
these terms
ceCS 575
3
Favor Composition Over Inheritance
For example:
Aggregation - when one object owns or is responsible
for another object and both objects have identical
lifetimes (GoF)
Aggregation - when one object has a collection of
objects that can exist on their own (UML)
Containment - a special kind of composition in
which the contained object is hidden from other
objects and access to the contained object is only via
the container object (Coad)

ceCS 575
4
Advantages/Disadvantages Of Composition
Advantages:
Contained objects are accessed by the containing class solely
through their interfaces
"Black-box" reuse, since internal details of contained objects are
not visible
Good encapsulation
Fewer implementation dependencies
Each class is focused on just one task
The composition can be defined dynamically at run-time through
objects acquiring references to other objects of the same type
Disadvantages:
Resulting systems tend to have more objects
Interfaces must be carefully defined in order to use many different
objects as composition blocks

ceCS 575
5
Inheritance
Method of reuse in which new functionality is obtained by
extending the implementation of an existing object
The generalization class (the base class) explicitly captures the
common attributes and methods
The specialization class (the derived class) extends the
implementation with additional attributes and methods
You can choose your associates, but youre stuck with your
ancestors.
Inheritance is often held to be sacrosanct in OOD.
Tendency for OO developers to gauge the success of their
efforts by the complexity of their inheritance hierarchy.


ceCS 575
6
Advantages/Disadvantages Of Inheritance
Advantages:
New implementation is easy, since most of it is inherited
Easy to modify or extend the implementation being reused
Disadvantages:
Breaks encapsulation, since it exposes a derived class to
implementation details of its base class
White-box" reuse, since internal details of base classes are often
visible to derived classes
Derived classes may have to be changed if the implementation of the
base class changes
Implementations inherited from base classes can not be changed at
runtime

ceCS 575
7
Inheritance occurs in two types
1. Implementation Inheritance
inheritance of implementation fragments/code from a base class.
2. Interface Inheritance
inheritance of contract fragments/interfaces.
Inheritance is a complex issue.
Basic notions differ among OO languages
Some controversial issues--e.g. multiple inheritance.
Inheritance can break encapsulation.
Poorly conceived inheritance relationships can frustrate system
reliability, maintainability, and evolvability.
Inheritance is neither inherently good or bad. It must be used
in a disciplined manner.

ceCS 575
8
A Wacky Inheritance Example
An example of multiple inheritance from an OO text

Of course, a pie is not a fruit
ceCS 575
9
Composed Pie
Pies (and most objects in the world) are combinations of
properties from divergent sources.
ceCS 575
10
Example Two
Inheritance is generally not appropriate for is a role played
by relationships.
For instance, consider roles in an airline reservation system:
passenger
ticket agent
flight crew
etc.
ceCS 575
11
Problems with Example 2
What happens if a pilot doubles as mechanic. And triples as a
ticket agent?
ceCS 575
12
Composition Version
Sometimes called delegation
ceCS 575
13
Inheritance Versus Composition--
Some Guidelines

It is generally not a good idea to use inheritance for the
following purposes:
To represent dynamically changing alternative roles of a base class
To hide methods or attributes inherited from a base class.
To implement a domain-specific class as a derived class of a utility
class.
Potential Drawbacks of Composition
There may be some minor performance penalty for invoking an
operation across object boundaries as opposed to using an inherited
method.
Delegation cant be used with partially abstract (uninstantiable)
classes
Delegation does not, in and of itself, impose any disciplined
structure on the design (but neither does a class hierarchy).
ceCS 575
14
Coad's Rules

Use inheritance only when all of the following criteria are
satisfied:
A derived class expresses "is a special kind of" and not "is a
role played by a"
An instance of a derived class never needs to become an object
of another class (transmutes)
A derived class extends, rather than overrides or nullifies, the
responsibilities of its base class
A derived class does not extend the capabilities of what is
merely a utility class
For a class in the actual Problem Domain, the derived class
specializes a role, transaction or device

ceCS 575
15
Inheritance VS Composition Example 1
ceCS 575
16
Inheritance VS Composition Example 1
"Is a special kind of" not "is a role played by a"
Fail. A passenger is a role a person plays. So is an agent.
Never needs to transmute
Fail. A instance of a subclass of Person could change from Passenger
to Agent to Agent Passenger over time
Extends rather than overrides or nullifies
Pass.
Does not extend a utility class
Pass.
Within the Problem Domain, specializes a role, transaction or
device
Fail. A Person is not a role, transaction or device.
Inheritance not appropriate in this situation
ceCS 575
17
Inheritance VS Composition Example 1
Use composition to complete the relationships
ceCS 575
18
Inheritance VS Composition Example 2
Is inheritance okay here?

ceCS 575
19
Inheritance VS Composition Example 2
"Is a special kind of" not "is a role played by a"
Pass. Passenger and agent are special kinds of person roles.
Never needs to transmute
Pass. A Passenger object stays a Passenger object; the same is true
for an Agent object.
Extends rather than overrides or nullifies
Pass.
Does not extend a utility class
Pass.
Within the Problem Domain, specializes a role, transaction or
device
Pass. A PersonRole is a type of role.
Inheritance ok here!
ceCS 575
20
Inheritance VS Composition Example 3
ceCS 575
21
Inheritance VS Composition Example 3
"Is a special kind of" not "is a role played by a"
Pass. Reservation and purchase are a special kind of transaction.
Never needs to transmute
Pass. A Reservation object stays a Reservation object; the same is
true for a Purchase object.
Extends rather than overrides or nullifies
Pass.
Does not extend a utility class
Pass.
Within the Problem Domain, specializes a role, transaction or
device
Pass. It's a transaction.
Inheritance is okay in this situation
ceCS 575
22
Inheritance vs. Composition Summary
Both composition and inheritance are important methods of
reuse
Inheritance was overused in the early days of OO development
Over time we've learned that designs can be made more
reusable and simpler by favoring composition
Of course, the available set of classes that can be composed can
be enlarged using inheritance
So composition and inheritance work together
But our fundamental principle is:
Favor Composition Over Inheritance

ceCS 575
23
Program To An Interface, Not An
Implementation

An interface is the set of methods one object knows it can
invoke on another object
An object can have many interfaces. (Essentially, an interface
is a subset of all the methods that an object implements).
A type is a specific interface of an object
Different objects can have the same type and the same object
can have many different types
An object is known by other objects only through its interface
In a sense, interfaces express "is a kind of" in a very limited
way as "is a kind of that supports this interface"
Interfaces are the key to reusability!

ceCS 575
24
Implementation Inheritance vs Interface
Inheritance
Implementation Inheritance (Class Inheritance) - an object's
implementation is defined in terms of another's objects
implementation
Interface Inheritance (Subtyping) - describes when one object
can be used in place of another object
The C++ inheritance mechanism means both class and interface
inheritance
C++ can perform interface inheritance by inheriting from a
pure abstract class
Java has a separate language construct for interface inheritance
- the Java interface
Java's interface construct makes it easier to express and
implement designs that focus on object interfaces

ceCS 575
25
Benefits Of Interfaces
Advantages:
Clients are unaware of the specific class of the object they
are using
One object can be easily replaced by another
Object connections need not be hardwired to an object of
a specific class, thereby increasing flexibility
Loosens coupling
Increases likelihood of reuse
Improves opportunities for composition since contained
objects can be of any class that implements a specific
interface
Disadvantages:
Modest increase in design complexity
ceCS 575
26
The Open-Closed Principle: Software Entities Should Be
Open For Extension, Yet Closed For Modification
The Open-Closed Principle (OCP) says that we should attempt to
design modules that never need to be changed
To extend the behavior of the system, we add new code. We do
not modify old code.
Modules that conform to the OCP meet two criteria:
Open For Extension - The behavior of the module can be extended to
meet new requirements
Closed For Modification - the source code of the module is not allowed
to change
How can we do this?
Abstraction
Polymorphism
Inheritance
Interfaces

ceCS 575
27
The Open-Closed Principle
It is not possible to have all the modules of a software system
satisfy the OCP, but we should attempt to minimize the number
of modules that do not satisfy it
The Open-Closed Principle is really the heart of OO design
Conformance to this principle yields the greatest level of
reusability and maintainability

ceCS 575
28
Open-Closed Principle Open-Closed
Principle Example

Consider the following method of some class:
public double totalPrice(Part[] parts) {
double total = 0.0;
for (int i=0; i<parts.length; i++) {
total += parts[i].getPrice();
}
return total;
}
The job of the above function is to total the price of each part in the
specified array of parts
If Part is a base class or an interface and polymorphism is being used, then
this class can easily accommodate new types of parts without having to be
modified!
It conforms to the OCP

ceCS 575
29
Open-Closed Principle Example ( Open-Closed Principle
Example (Continued)

But what if the Accounting Department decrees that motherboard
parts and memory parts should have a premium applied when
figuring the total price.
How about the following code?
public double totalPrice(Part[] parts) {
double total = 0.0;
for (int i=0; i<parts.length; i++) {
if (parts[i] instanceof Motherboard)
total += (1.45 * parts[i].getPrice());
else if (parts[i] instanceof Memory)
total += (1.27 * parts[i].getPrice());
else
total += parts[i].getPrice();
}
return total;
}
ceCS 575
30
Open-Closed Principle Example ( Open-Closed Principle
Example (Continued)
Does this conform to the OCP? No way!
Every time the Accounting Department comes out with a new
pricing policy, we have to modify the totalPrice() method! It is
not Closed For Modification. Obviously, policy changes such
as that mean that we have to modify code somewhere, so what
could we do?
To use our first version of totalPrice(), we could incorporate
pricing policy in the getPrice() method of a Part

ceCS 575
31
Closed for Modification? Open for Extension?
#include <iostream>
using namespace std;

class pizza {
public:
virtual float cost()=0 {}
};

class small_pizza : public pizza {
public:
float cost(){
return 10.0;
}
};


class medium_pizza : public
pizza {
public:
float cost(){
return 12.0;
}
};
enum psize {none, small, medium};
pizza *factory(int size) {
if(size==small)
return new small_pizza;
else
if(size==medium)
return new medium_pizza;
else
return none;
}
int main(){
pizza *p;
int size;

cout <<
"What size pizza? (small = 1, medium = 2) - " ;
cin >> size;

p=factory(size);
cout << "Cost = $" << p->cost() << endl;
return 0;
}
ceCS 575
32
The Liskov Substitution Principle:
Functions That Use References To Base (Super)
Classes Must Be Able To Use Objects Of Derived
(Sub) Classes Without Knowing It
In intent, Liskov Substitution Principle (LSP) forces
software designers to not override base class
functionality if it affects the performance of
components designed to work with the base class.

ceCS 575
33
If S is a subtype of T, then objects of type
T may be replaced with objects of type S
without altering the correctness of the
program.

Definition
ceCS 575
34
Example
class W {
(..)
void use (T obj) {
String s = obj.getInfo()
}
(..)
}

objW.use(objT)
/* no error is introduced if S is
compliant with the principle*/
objW.use(objS)

ceCS 575
35
Example
ceCS 575
36
Example
/*Method to print information
about someone on the screen*/

void printInfo(Person obj) { ()

parser.parseName(obj.getName());
() }

A programmer may write
expecting this code to work for all derived types of
Person, including Employee. This code will
actually result in error.

The redefinition of getName changed the behavior
of the method, and introduced an error (or several
errors) in the program.
ceCS 575
37
LSP Summary
In order for the LSP to hold (and with it the Open-Closed
Principle) all subclasses must conform to the behavior that clients
expect of the base classes they use
A subtype must have no more constraints than its base type, since
the subtype must be usable anywhere the base type is usable
If the subtype has more constraints than the base type, there would
be uses that would be valid for the base type, but that would
violate one of the extra constraints of the subtype and thus violate
the LSP!
The guarantee of the LSP is that a subclass can always be used
wherever its base class is used!

ceCS 575
38
Minimize The Accessibility of Classes and Members

We must define several key concepts:
abstraction,
information hiding, and
encapsulation,
All three are different, independent characteristics, and all
three are possible in non-OO languages
ceCS 575
39
Abstraction
"Abstraction is the selective examination of certain aspects of a
problem. The goal of abstraction is to isolate those aspects that are
important for some purpose and suppress those aspects that are
unimportant."
-- [Rumbaugh et al, 1991]
"A view of a problem that extracts the essential information
relevant to a particular purpose and ignores the remainder of the
information."
-- [IEEE, 1983]
"An abstraction denotes the essential characteristics of an object
that distinguish it from all other kinds of object and thus provide
crisply defined conceptual boundaries, relative to the perspective of
the viewer."
-- [Booch, 1991]
Abstraction is one of the fundamental ways to deal with complexity
ceCS 575
40
Information Hiding
The second decomposition was made using 'information hiding'
... as a criterion. The modules no longer correspond to steps in
the processing. ... Every module in the second decomposition is
characterized by its knowledge of a design decision which it
hides from all others. Its interface or definition was chosen to
reveal as little as possible about its inner workings."
-- [Parnas, 1972b]
"The process of hiding all the details of an object that do not
contribute to its essential characteristics; typically, the structure
of an object is hidden, as well as the implementation of its
methods. The terms information hiding and encapsulation are
usually interchangeable." -- [Booch, 1991]
"... [I]nformation hiding: a module is characterized by the
information it hides from other modules, which are called its
clients. The hidden information remains a secret to the client
modules." -- [Ghezzi et al, 1991]
ceCS 575
41
Encapsulation
"[E]ncapsulation -- also known as information hiding -- prevents
clients from seeing its inside view, were the behavior of the
abstraction is implemented."
-- [Booch, 1991]
"Encapsulation (also information hiding) consists of separating
the external aspects of an object which are accessible to other
objects, from the internal implementation details of the object,
which are hidden from other objects."
-- [Rumbaugh et al, 1991]
"The concept of encapsulation as used in an object-oriented
context is not essentially different from its dictionary definition.
It still refers to building a capsule, in the case a conceptual
barrier, around some collection of things."
-- [Wirfs-Brock et al, 1990]
ceCS 575
42
Use Accessors and Mutators, Not Public Members
Use private members and appropriate accessors and mutators wherever possible
For example:
Replace
public double speed;
with
private double speed;
public double getSpeed() {
return(speed);
}
public void setSpeed(double newSpeed) {
speed = newSpeed;
}
ceCS 575
43
Benefits of Information Hiding
You can put constraints on values
public void setSpeed(double newSpeed) {
if (newSpeed < 0) {
sendErrorMessage(...);
newSpeed = Math.abs(newSpeed);
}
speed = newSpeed;
}
If users of your class accessed the fields directly, then they
would each be responsible for checking constraints
ceCS 575
44
Benefits of Information Hiding
You can change your internal representation without changing the interface
// Now using metric units (kph, not mph)
public void setSpeedInMPH(double newSpeed) {
speedInKPH = convert(newSpeed);
}
public void setSpeedInKPH(double newSpeed) {
speedInKPH = newSpeed;
}

ceCS 575
45
Benefits of Information Hiding
You can perform arbitrary side effects
public double setSpeed(double newSpeed) {
speed = newSpeed;
notifyObservers();
}
If users of your class accessed the fields directly, then they
would each be responsible for executing side effects

ceCS 575
46
The Law of Demeter
Only talk to your immediate friends.
An object should assume as little as possible about the structure
or properties of anything else (including its subcomponents).
More formally, the Law of Demeter for functions requires that
a method M of an object O may only invoke the methods of the
following kinds of objects:
O itself
M's parameters
any objects created/instantiated within M
O's direct component (aggregated) objects

ceCS 575
47
Demeter Example
class C {
public:
void HiC(){
return;
}
};

class P {
public:
void op1(){
return;
}
};

class DontTalk2Me {
public:
void DontTalkToMe(){
return;
}
};

DontTalk2Me dt2m;

class O {
C cobj;
public:
void op1(P pobj) {
pobj.op1();
cobj.HiC();
dt2m.DontTalkToMe();
}
Void op2() {
op1();
};

int main(){
O obj;
P p;

obj.op1(p);
return 0;
}

ceCS 575
48
The Dependency Inversion Principle
A. High level modules should not depend upon low level
modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details
should depend upon abstractions.

High level modules that contain the important policy decisions and
business models of an application
If high level modules depend upon the lower level modules, then
changes to the lower level modules can have direct effects upon them
and can force them to change
High level modules simply should not depend upon low level modules
in any way
ceCS 575
49
How to Invert Dependency?
1. Find a direct dependency between a high level and low level
module
2. Remove references to the low level modules in the high levels
3. Introduce an abstraction to represent the low level functions to
the high level module
4. Compose the abstraction in the high level module
5. Derive the low level modules from the abstraction and
override the necessary methods listed in the abstraction
6. Let polymorphism redirect calls to the abstract methods to the
concrete methods
ceCS 575
50
Simple Example
// Dependency Inversion Principle - Bad example
class Worker {
public void work() {
// ....working
}
}

class Manager {
Worker m_worker;

public void setWorker(Worker w) {
m_worker=w;
}

public void manage() {
m_worker.work();
}
}

class SuperWorker {
public void work() {
//.... working much more
}
}

// Dependency Inversion Principle - Good example
interface IWorker {
public void work();
}

class Worker implements IWorker{
public void work() {
// ....working
}
}

class SuperWorker implements IWorker{
public void work() {
//.... working much more
}
}

class Manager {
IWorker m_worker;
public void setWorker(IWorker w) {
m_worker=w;
}

public void manage() {
m_worker.work();
}
}

You might also like