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

2.

Object-Oriented Design Principles


OO Design Principles

Roadmap

> Motivation: stability in the face of change


> Model domain objects
> Model responsibilities
> Separate interface and implementation
> Delegate responsibility
> Let the code talk
> Recognize Code Smells

© Oscar Nierstrasz 2
OO Design Principles

Roadmap

> Motivation: stability in the face of change


> Model domain objects
> Model responsibilities
> Separate interface and implementation
> Delegate responsibility
> Let the code talk
> Recognize Code Smells

© Oscar Nierstrasz 3
OO Design Principles

Motivation

The law of continuing change:


A large program that is used undergoes continuing
change or becomes progressively less useful.
The change process continues until it is judged
more cost-effective to replace the system with a
recreated version.
— Lehman and Belady, 1985

© Oscar Nierstrasz 4
OO Design Principles

What should design optimize?

Enable small, incremental changes by designing software


around stable abstractions and interchangeable parts.
© Oscar Nierstrasz 5
OO Design Principles

How do we find the “right” design?

Object-oriented design is an iterative and exploratory process

Don’t worry if your initial design is ugly.


If you apply the OO design principles
consistently, your final design will be beautiful!

© Oscar Nierstrasz 6
OO Design Principles

Running Example: Snakes and Ladders

http://en.wikipedia.org/wiki/Snakes_and_ladders

© Oscar Nierstrasz 7
OO Design Principles

Game rules

> Players
— Snakes and Ladders is played by two to four players, each with her own token
to move around the board.
> Moving
— Players roll a die or spin a spinner, then move the designated number of
spaces, between one and six. Once they land on a space, they have to perform
any action designated by the space.
> Ladders
— If the space a player lands on is at the bottom of a ladder, he should climb the
ladder, which brings him to a space higher on the board.
> Snakes
— If the space a player lands on is at the top of a snake, she must slide down to
the bottom of it, landing on a space closer to the beginning.
> Winning
— The winner is the player who gets to the last space on the board first,
whether by landing on it from a roll, or by reaching it with a ladder.

http://www.ehow.com/facts_5163203_snakes-amp-ladders-rules.html
© Oscar Nierstrasz 8
OO Design Principles

Variations

> A player who lands on an occupied square must go back


to the start square.
> If you roll a number higher than the number of squares
needs to reach the last square, you must continue
moving backwards.
> …

© Oscar Nierstrasz 9
OO Design Principles

Roadmap

> Motivation: stability in the face of change


> Model domain objects
> Model responsibilities
> Separate interface and implementation
> Delegate responsibility
> Let the code talk
> Recognize Code Smells

© Oscar Nierstrasz 10
OO Design Principles

Programming is modeling

Model domain objects

Game Board Square Snake End Square

Players

Start Square Die


Ladder

What about roll, action, winner … ?


© Oscar Nierstrasz 11
OO Design Principles

Everything is an object

Every domain concept that plays a role in the


application and assumes a responsibility is a
potential object in the software design

“Winner” is just a state of a player


— it has no responsibility of its own.

© Oscar Nierstrasz 12
OO Design Principles

Computation is simulation

“Instead of a bit-grinding processor … plundering


data structures, we have a universe of well-
behaved objects that courteously ask each
other to carry out their various desires.”
— Ingalls 1981

© Oscar Nierstrasz 13
OO Design Principles

Model specialization

The first square is a kind of square, so model it as such

Is a snake a kind of reverse ladder?

© Oscar Nierstrasz 14
OO Design Principles

Roadmap

> Motivation: stability in the face of change


> Model domain objects
> Model responsibilities
> Separate interface and implementation
> Delegate responsibility
> Let the code talk
> Recognize Code Smells

© Oscar Nierstrasz 15
OO Design Principles

Responsibility-Driven Design

Well-designed objects have clear responsibilities

Drive design by asking:


• What actions is this object responsible for?
• What information does this object share?

Responsibility-driven
Responsibility-driven design
design ...
... minimizes
minimizes the
the
rework
rework required
required for
for major
major design
design changes.
changes.

— Wirfs-Brock,
Wirfs-Brock, 1989
1989

© Oscar Nierstrasz 16
OO Design Principles

Snakes and Ladders responsibilities

Player
•keeps track of where it is
Game
•moves over squares of the
•keeps track of the game state
board
Square Die
•keeps track of any player on it •provides a random
number from 1 to 6
First Square
•can hold multiple players Last Square
•knows it is the winning square
Snake
•sends a player back to Ladder
an earlier square •sends a player ahead to
a later square
© Oscar Nierstrasz 17
OO Design Principles

The Single Responsibility Principle

An object should have no more than one key responsibility.

If an object has several, unrelated responsibilities,


then you are missing objects in your design!

The different kinds of squares have


separate responsibilities, so they must
belong to separate classes!
http://en.wikipedia.org/wiki/Single_responsibility_principle

© Oscar Nierstrasz 18
OO Design Principles

Top-down decomposition
Use concrete scenarios to drive interface design
jack
jack == new
new Player("Jack");
Player("Jack");
jill
jill == new
new Player("Jill");
Player("Jill");
Player[]
Player[] args
args == {{ jack,
jack, jill
jill };
};
Game
Game gamegame == new
new Game(12,
Game(12, args);
args);
game.setSquareToLadder(2,
game.setSquareToLadder(2, 4); 4);
game.setSquareToLadder(7,
game.setSquareToLadder(7, 2); 2);
game.setSquareToSnake(11,
game.setSquareToSnake(11, -6); -6);
assertTrue(game.notOver());
assertTrue(game.notOver());
assertTrue(game.firstSquare().isOccupied());
assertTrue(game.firstSquare().isOccupied());
assertEquals(1,
assertEquals(1, jack.position());
jack.position());
assertEquals(1,
assertEquals(1, jill.position());
jill.position());
assertEquals(jack,
assertEquals(jack, game.currentPlayer());
game.currentPlayer());

game.movePlayer(4);
game.movePlayer(4);
assertTrue(game.notOver());
assertTrue(game.notOver());
assertEquals(5,
assertEquals(5, jack.position());
jack.position());
assertEquals(1,
assertEquals(1, jill.position());
jill.position());
assertEquals(jill,
assertEquals(jill, game.currentPlayer());
game.currentPlayer());

© Oscar Nierstrasz http://en.wikipedia.org/wiki/Top-down_and_bottom-up_design 19


OO Design Principles

Jack makes a move

© Oscar Nierstrasz 20
OO Design Principles

Roadmap

> Motivation: stability in the face of change


> Model domain objects
> Model responsibilities
> Separate interface and implementation
> Delegate responsibility
> Let the code talk
> Recognize Code Smells

© Oscar Nierstrasz 21
OO Design Principles

Separate interface and implementation

Information hiding: a component should provide all and


only the information that the user needs to effectively use it.

Information hiding protects both the provider and


the client from changes in the implementation.

http://en.wikipedia.org/wiki/Information_hiding
© Oscar Nierstrasz 22
OO Design Principles

Abstraction, Information Hiding and


Encapsulation

Abstraction = elimination of inessential detail

Information hiding = providing only the


information a client needs to know

Encapsulation = bundling operations to


access related data as a data abstraction

In object-oriented languages we can


implement data abstractions as classes.

© Oscar Nierstrasz 23
OO Design Principles

Encapsulate state

public
public class
class Game
Game {{
private
private List<ISquare>
List<ISquare> squares;
squares;
private
private int
int size;
size; Don't let anyone
private
private Queue<Player>
Queue<Player> players;
players; else play with you.
private
private Player
Player winner;
winner;
...
...
— Joseph Pelrine
}} public
public class
class Player
Player {{
private
private String
String name;
name;
private
private ISquare
ISquare square;
square;
...
...
}}
public
public class
class Square
Square implements
implements ISquare
ISquare {{
protected
protected intint position;
position;
protected
protected Game
Game game;
game;
private
private Player
Player player;
player;
...
...
}}

© Oscar Nierstrasz 24
OO Design Principles

Keep behaviour close to state

public
public class
class Square
Square implements
implements ISquare
ISquare {{
private
private Player
Player player;
player;

public
public boolean
boolean isOccupied()
isOccupied() {{
return
return player
player !=
!= null;
null;
}}

public
public void
void enter(Player
enter(Player player)
player) {{
this.player
this.player == player;
player;
}}

public
public void
void leave(Player
leave(Player _)
_) {{
this.player
this.player == null;
null;
}}
...
...
}}

© Oscar Nierstrasz 25
OO Design Principles

Program to an interface, not an implementation


public
public interface
interface ISquare
ISquare {{
public
public int
int position();
position();
public
public ISquare
ISquare moveAndLand(int
moveAndLand(int moves);
moves);
Depend on public
public boolean
boolean isFirstSquare();
isFirstSquare();
public
public boolean
boolean isLastSquare();
isLastSquare();
interfaces, not public
public void
void enter(Player
enter(Player player);
player);
concrete classes public
public void
void leave(Player
leave(Player player);
player);
public
public boolean
boolean isOccupied();
isOccupied();
public
public ISquare
ISquare landHereOrGoHome();
landHereOrGoHome();
}}

public
public class
class Player
Player {{
private
private ISquare
ISquare square;
square; Players do not
public
public void
void moveForward(int
moveForward(int moves)
moves) {{
square.leave(this);
need to know all
square.leave(this);
square
square == square.moveAndLand(moves);
square.moveAndLand(moves); the different kinds
square.enter(this);
square.enter(this); of squares …
}} ...
...
}} © Oscar Nierstrasz 26
OO Design Principles

Aside: Messages and methods


Objects send messages to one another;
they don’t “call methods”

public
public class
class Square
Square implements
implements ISquare
ISquare {{
private
private Player
Player player;
player;

public
public void
void enter(Player
enter(Player player)
player) {{
this.player
this.player == player;
player;
}}
...
...
}} public
public class
class FirstSquare
FirstSquare extends
extends Square
Square {{
private
private List<Player>
List<Player> players;
players;

Clients should not public


public void
void enter(Player
enter(Player player)
player) {{
care what kind of players.add(player);
players.add(player);
}}
square they occupy ...
...
}}
© Oscar Nierstrasz 27
OO Design Principles

The Open-Closed Principle


Make software entities
open for extension but
closed for modifications.
public
public class
class Square
Square implements
implements ISquare
ISquare {{
public
public ISquare
ISquare moveAndLand(int
moveAndLand(int moves) moves) {{
return
return game.findSquare(position,
game.findSquare(position, moves).landHereOrGoHome();
moves).landHereOrGoHome();
}}
public
public ISquare
ISquare landHereOrGoHome()
landHereOrGoHome() {{
return
return this.isOccupied()
this.isOccupied() ?? game.firstSquare()
game.firstSquare() :: thisthis ;;
}}
...
...
}} public
public class
class Ladder
Ladder extends
extends Square
Square {{
public
public ISquare
ISquare landHereOrGoHome()
landHereOrGoHome() {{
return
return this.destination().landHereOrGoHome();
this.destination().landHereOrGoHome();
}}
protected
protected ISquare
ISquare destination()
destination() {{
return
return game.getSquare(position+transport);
game.getSquare(position+transport);
}}
...
...
}} http://en.wikipedia.org/wiki/Open/closed_principle

© Oscar Nierstrasz 28
OO Design Principles

Why are data abstractions important?

Communication — Declarative Programming


> Data abstractions …
— State what a client needs to know, and no more!
— State what you want to do, not how to do it!
— Directly model your problem domain

Software Quality and Evolution


> Data abstractions …
— Decompose a system into manageable parts
— Protect clients from changes in implementation
— Encapsulate client/server contracts
— Can extend their interfaces without affecting clients
— Allow new implementations to be added transparently to a system

© Oscar Nierstrasz 29
OO Design Principles

Roadmap

> Motivation: stability in the face of change


> Model domain objects
> Model responsibilities
> Separate interface and implementation
> Delegate responsibility
> Let the code talk
> Recognize Code Smells

© Oscar Nierstrasz 30
OO Design Principles

Delegate responsibility “Don’t do anything you can


push off to someone else.”
— Joseph Pelrine
public
public class
class Player
Player {{
public
public void
void moveForward(int
moveForward(int moves) moves) {{ Responsibility implies
square.leave(this);
square.leave(this); non-interference.
square
square == square.moveAndLand(moves);
square.moveAndLand(moves);
square.enter(this);
— Timothy Budd
square.enter(this);
}}
...
... public
public class
class Square
Square implements
implements ISquare
ISquare {{
}} public
public ISquare
ISquare moveAndLand(int
moveAndLand(int moves) moves) {{
return
return game.findSquare(position,
game.findSquare(position, moves) moves)
.landHereOrGoHome();
.landHereOrGoHome();
}}
...
... public
public class
class Game
Game {{
}} public
public ISquare
ISquare findSquare(...)
findSquare(...) {{
...
...
return
return this.getSquare(target);
this.getSquare(target);
}}
...
...
© Oscar Nierstrasz }} 31
OO Design Principles

Lots of short methods

Once and only once


“In a program written with good style, everything
is said once and only once.”

Lots of little pieces


“Good code invariably has small methods and
small objects. Only by factoring the system into
many small pieces of state and function can you
hope to satisfy the ‘once and only once’ rule.”

http://c2.com/cgi/wiki?LotsOfShortMethods
© Oscar Nierstrasz 32
OO Design Principles

Composed Method

Divide your program into methods


that perform one identifiable task.

• Keep all of the operations in a method at the


same level of abstraction.
• This will naturally result in programs with
many small methods, each a few lines long.

© Oscar Nierstrasz 33
OO Design Principles

Maintain a consistent level of abstraction …

public
public class
class Game
Game {{
public
public void
void play(Die
play(Die die)
die) {{
System.out.println("Initial
System.out.println("Initial state:
state: "" +
+ this);
this);
while
while (this.notOver())
(this.notOver()) {{
int
int roll
roll == die.roll();
die.roll();
System.out.println(this.currentPlayer()
System.out.println(this.currentPlayer()
+
+ "" rolls
rolls "" +
+ roll
roll ++ ":
": "" +
+ this);
this);
this.movePlayer(roll);
this.movePlayer(roll);
}}
System.out.println("Final
System.out.println("Final state:
state: "" + + this);
this);
System.out.println(this.winner()
System.out.println(this.winner() + + "" wins!");
wins!");
}}
...
...

© Oscar Nierstrasz 34
OO Design Principles

… to obtain many small methods

public
public boolean
boolean notOver()
notOver() {{
public
public Player
Player currentPlayer()
currentPlayer() {{
return
return winner
winner ==
== null;
null;
return
return players.peek();
players.peek();
}}
}}

public
public void
void movePlayer(int
movePlayer(int roll)
roll) {{
Player
Player currentPlayer
currentPlayer == players.remove();
players.remove(); //// from
from front
front of
of queue
queue
currentPlayer.moveForward(roll);
currentPlayer.moveForward(roll);
players.add(currentPlayer);
players.add(currentPlayer); //// to
to back
back of
of the
the queue
queue
ifif (currentPlayer.wins())
(currentPlayer.wins()) {{
winner
winner == currentPlayer;
currentPlayer;
}}
}}

public
public Player
Player winner()
winner() {{
return
return winner;
winner;
}}

© Oscar Nierstrasz 35
OO Design Principles

… and simple classes

public
public class
class Die
Die {{
static
static final
final int
int MIN
MIN == 1;
1;
static
static final
final int
int MAX
MAX == 6;
6;

public
public int
int roll()
roll() {{
return
return this.random(MIN,MAX);
this.random(MIN,MAX);
}}

public
public int
int random(int
random(int min,
min, int
int max)
max) {{
int
int result
result == (int)
(int) (min
(min ++ Math.floor((max-min)
Math.floor((max-min) ** Math.random()));
Math.random()));
return
return result;
result;
}}
}}

© Oscar Nierstrasz 36
OO Design Principles

Snakes and Ladders methods

• 76 methods
• Most are 1 or 2 LOC
• Average 3.2 LOC
• All methods > 5 LOC
are tests or algorithms

© Oscar Nierstrasz 37
OO Design Principles

Design by Contract = Don't accept anybody


else's garbage!
public
public class
class Game
Game {{ More on this in the
public
public void
void movePlayer(int
movePlayer(int roll) roll) {{ following lecture
assert
assert roll>=1
roll>=1 && && roll<=6;
roll<=6;
...
...
}}
public
public class
class Player
Player {{
...
... public
public void
void moveForward(int
moveForward(int moves) moves) {{
}}
assert
assert moves
moves > > 0;0;
...
...
}} public
public class
class Square
Square implements
implements ISquare
ISquare {{
... public
public ISquare
ISquare moveAndLand(int
moveAndLand(int moves)
moves) {{
...
}} assert
assert moves
moves >=
>= 0;
0;
...
...
}}
...
...
}}

© Oscar Nierstrasz http://en.wikipedia.org/wiki/Design_by_contract 38


OO Design Principles

Demo
public
public static
static void
void main(String
main(String args[])
args[]) {{
(new
(new SimpleGameTest()).newGame().play(new
SimpleGameTest()).newGame().play(new Die());
Die());
}}

© Oscar Nierstrasz 39
OO Design Principles

Roadmap

> Motivation: stability in the face of change


> Model domain objects
> Model responsibilities
> Separate interface and implementation
> Delegate responsibility
> Let the code talk
> Recognize Code Smells

© Oscar Nierstrasz 40
OO Design Principles

Program declaratively

Name objects and methods so that code documents itself

public
public class
class Player
Player {{
public
public void
void joinGame(Game
joinGame(Game game)
game) {{
square
square == game.getSquare(1);
game.getSquare(1);
((FirstSquare)
((FirstSquare) square).players().add(this);
square).players().add(this);
}}
...
...
}} public
public class
class Player
Player {{
public
public void
void joinGame(Game
joinGame(Game game)
game) {{

?!
square
square == game.firstSquare();
game.firstSquare();
square.enter(this);
square.enter(this);
}}
...
...
}}
© Oscar Nierstrasz 41
OO Design Principles

Role Suggesting Instance Variable Name

Name instance variables for the


role they play in the computation.

public
public class
class Game
Game {{
private
private List<ISquare>
List<ISquare> squares;
squares;
private
private int
int size;
size;
private
private Queue<Player>
Queue<Player> players;
players;
private
private Player
Player winner;
winner;
...
...
}}

Make the name plural if the


variable will hold a collection.
© Oscar Nierstrasz 42
OO Design Principles

Intention Revealing Method Name


public
public class
class Player
Player {{
public
public void
void moveForward(int
moveForward(int moves)
moves) {{
...
...
square.enter(this);
square.enter(this);
}} Name methods after what
...
... they accomplish, not how.
}}

public
public class
class Square
Square implements
implements ISquare
ISquare {{
private
private Player
Player player;
player;
public
public void
void enter(Player
enter(Player player)
player) {{
this.player
this.player == player;
player;
}}
...
... public
public class
class FirstSquare
FirstSquare extends
extends Square
Square {{
}} private
private List<Player>
List<Player> players;
players;
public
public void
void enter(Player
enter(Player player)
player) {{
players.add(player);
players.add(player);
}}
...
...
}}
© Oscar Nierstrasz 43
OO Design Principles

Roadmap

> Motivation: stability in the face of change


> Model domain objects
> Model responsibilities
> Separate interface and implementation
> Delegate responsibility
> Let the code talk
> Recognize Code Smells

© Oscar Nierstrasz 44
OO Design Principles

The Law of Demeter: “Do not talk to strangers”

Don’t send messages to objects


returned from other message sends

public
public void
void movePlayer(int
movePlayer(int roll)
roll) {{
...
...
ifif (currentPlayer.square().isLastSquare())
(currentPlayer.square().isLastSquare()) {{
winner
winner == currentPlayer;
currentPlayer;
}}
}}
public
public void
void movePlayer(int
movePlayer(int roll)
roll) {{
...
...
ifif (currentPlayer.wins())
(currentPlayer.wins()) {{
winner
winner == currentPlayer;
currentPlayer;
}}
}}
Tell, don't ask

© Oscar Nierstrasz en.wikipedia.org/wiki/Law_Of_Demeter 45


OO Design Principles

Be sensitive to Code Smells

> Duplicated Code


— Missing inheritance or delegation
> Long Method
— Inadequate decomposition
> Large Class / God Class
— Too many responsibilities
> Long Parameter List
— Object is missing
> Feature Envy
— Method needing too much information from another object
> Data Classes
— Only accessors

http://en.wikipedia.org/wiki/Code_smell
© Oscar Nierstrasz 46
OO Design Principles

Conclusions and outlook

> Use responsibility-driven design to stabilize domain concepts


> Delegate responsibility to achieve simple, flexible designs

> Specify contracts to protect your data abstractions


— Design by Contract lecture
> Express your assumptions as tests to tell what works and doesn’t
— Testing Framework lecture
> Develop iteratively and incrementally to allow design to emerge
— Iterative Development lecture
> Encode specialization hierarchies using inheritance
— Inheritance lecture

© Oscar Nierstrasz 47
Safety Patterns

What you should know!

 Why does software change?


 Why should software model domain concepts?
 What is responsibility-driven design?
 How do scenarios help us to design interfaces?
 What is the difference between abstraction,
encapsulation and information hiding?
 Can you explain the Open-Closed principle?
 How can delegation help you write declarative code?
 How should you name methods and instance variables?

© Oscar Nierstrasz 48
Safety Patterns

Can you answer these questions?

 How do you identify responsibilities?


 How can we use inheritance to model the relationship
between Snakes and Ladders?
 How can we tell if an object has too many
responsibilities?
 Is top-down design better than bottom-up design?
 Why should methods be short?
 How does the Law of Demeter help you to write flexible
software?
 Why do “God classes” and Data classes often occur
together?

© Oscar Nierstrasz 49
OO Design Principles

License

http://creativecommons.org/licenses/by-sa/3.0/

Attribution-ShareAlike 3.0 Unported


You are free:
to Share — to copy, distribute and transmit the work
to Remix — to adapt the work

Under the following conditions:


Attribution. You must attribute the work in the manner specified by the author or
licensor (but not in any way that suggests that they endorse you or your use of the
work).
Share Alike. If you alter, transform, or build upon this work, you may distribute the
resulting work only under the same, similar or a compatible license.
For any reuse or distribution, you must make clear to others the license terms of this work.
The best way to do this is with a link to this web page.
Any of the above conditions can be waived if you get permission from the copyright holder.
Nothing in this license impairs or restricts the author's moral rights.

© Oscar Nierstrasz 50

You might also like