examples of good and bad oo designs - cs csu...

15
1 ©Robert B. France 1 Examples of Good and Bad OO Designs Robert B. France Colorado State University Fort Collins Colorado ©Robert B. France 2 Section Outline Problem Description A novice design Improving the design

Upload: vanlien

Post on 19-Jun-2019

216 views

Category:

Documents


0 download

TRANSCRIPT

1

©Robert B. France 1

Examples of Good and Bad OO Designs

Robert B. France

Colorado State University

Fort Collins

Colorado

©Robert B. France 2

Section Outline

• Problem Description

• A novice design

• Improving the design

2

©Robert B. France 3

The Problem• [Variation of a program example used in the book

“Refactoring” by Martin Fowler]

• Program calculates and prints a statement of a customer’s charges at a video store. There are three types of movies: Regular, NewRelease, Children.

• Rates– Regular: 2.00 for 2 days; late 1.50/late day

– Children: 1.50 for 3 days; late 1.50/late day

– NewRelease: 3.00/day

©Robert B. France 4

Design 1: Class Diagram

Customer

Rental Movie

makes

refers_to

_rentals

_movie

1

*

*

1

name: String

addRental()getName()statement()

daysrented:int

getDaysRented()getMovie()

title:StringpriceCode:int

getPriceCode()setPriceCode()getTitle()

3

©Robert B. France 5

Design 1: Sequence Model (with activation boxes)

©Robert B. France 6

Design 1: Code for MoviePubl i c c l ass Movi e{

publ i c s t at i c f i nal i nt CHI LDREN = 2;

publ i c s t at i c f i nal i nt REGULAR = 0;

publ i c s t at i c f i nal i nt NEW = 1;

pr i vat e st r i ng _t i t l e;

pr i vat e i nt _pr i ceCode;

publ i c Movi e ( St r i ng t i t l e, i nt pr i ceCode) {

_t i t l e = t i t l e; _pr i ceCode=pr i ceCode; }

publ i c i nt get Pr i ceCode( ) {

r et ur n _pr i ceCode; }

publ i c i nt get Ti t l e( ) {

r et ur n _t i t l e; }

publ i c voi d set Pr i ceCode( i nt ar g) {

_pr i ceCode=ar g; }

}

4

©Robert B. France 7

Code for Rental

cl ass Rent al {

pr i vat e Movi e _movi e;

pr i vat e i nt _daysRent ed;

publ i c Rent al ( Movi e movi e, i nt daysRent ed) {

_movi e=movi e; _daysRent ed=daysRent ed; }

publ i c i nt get DaysRent ed( ) {

r et ur n _daysRent ed; }

publ i c Movi e get Movi e( ) {

r et ur n _movi e; }

}

©Robert B. France 8

Code for Customercl ass Cust omer {

pr i vat e st r i ng _name;pr i vat e vect or _r ent al s = new Vect or ( ) ;

publ i c Cust omer ( St r i ng name) {_name = name; }

publ i c voi d addRent al ( Rent al ar g) {_r ent al s. addEl ement ( ar g) ; }

publ i c St r i ng get Name( ) {r et ur n _name; }

publ i c St r i ng st at ement ( ) { …}

}

5

©Robert B. France 9

Code Skeleton for statement() Derived from Sequence Model

publ i c St r i ng St at ement ( ) {Enumer at i on r ent al s = _r ent al s. el ement s( ) ;\ …

whi l e( r ent al s. hasMor eEl ement s( ) ) {\ \ . . .

Rent al R =( Rent al ) r ent al s. next El ement ( ) ;\ \

Movi e M=aRent al . get Movi e( ) ;i nt PC=M. get Pr i ceCode( ) ;St r i ng T = M. get Ti t l e( ) ;i nt days=R. get DaysRent ed( ) ;

}\ \ …

}

©Robert B. France 10

Code for statement()publ i c St r i ng St at ement ( ) {

doubl e t ot al Amt = 0;Enumer at i on r ent al s = _r ent al s. el ement s( ) ;St r i ng r esul t = “ Recor d f or ” +get Name( ) +” \ n” ;whi l e( r ent al s. hasMor eEl ement s( ) ) {

Rent al aRent al =( Rent al ) r ent al s. next El ement ( ) ;t hi sAmt =get Amount ( aRent al ) ;St r i ng t =aRent al . get Movi e( ) . get Ti t l e( ) ;r esul t += ” \ t ” + t + St r i ng. val ueOf ( t hi sAmt ) +” \ n” ;t ot al Amt =t ot al Amt +t hi sAmt ;

}r esul t +=” Amount owed i s” +St r i ng. val ueOf ( t ot al Amt ) +” \ n” ;r et ur n r esul t ;

}

6

©Robert B. France 11

Code for getAmount()Pr i vat e doubl e get Amount ( Rent al aRent al ) {

Mov i e aMov i e=aRent al . get Mov i e( ) ;

i nt pc=aMovi e. get Pr i ceCode( ) ;i nt days=aRent al . get DaysRent ed( ) ;

i nt t hi sAmt = 0;swi t ch ( pc) {

case Movi e. REGULAR:t hi sAmt = 2;

i f ( days > 2)t hi sAmt += ( days- 2) * 1. 5;

br eak ;

case Movi e. NEW:t hi sAmt = days* 3;

br eak ;case Movi e. CHI LDREN:

t hi sAmt = 1. 5;i f ( days > 3)

t hi sAmt += ( days- 3) * 1. 5;br eak ;

}r et ur n t hi sAmt ;

}

©Robert B. France 12

Modified Sequence Model (with activation boxes)

7

©Robert B. France 13

Tidying Up: statement()publ i c St r i ng St at ement ( ) {

doubl e t ot al Amt = 0;

Enumer at i on r ent al s = _r ent al s. el ement s( ) ;

St r i ng r esul t = “ Recor d f or ” +get Name( ) +” \ n” ;

whi l e( r ent al s. hasMor eEl ement s( ) ) {

Rent al aRent al =( Rent al ) r ent al s. next El ement ( ) ;

t hi sAmt =get Amount ( aRent al ) ;

r esul t += ” \ t ” +aRental.getMovie().getTitle()+

St r i ng. val ueOf ( t hi sAmt ) +” \ n” ;

t ot al Amt =t ot al Amt +t hi sAmt ;

}

r esul t +=” Amount owed

i s” +St r i ng. val ueOf ( t ot al Amt ) +” \ n” ;

r et ur n r esul t ;

}

©Robert B. France 14

Tidying Up: getAmount()Pr i vat e doubl e get Amount ( Rent al aRent al ) {

Mov i e aMov i e=aRent al . get Mov i e( ) ;

i nt days=aRent al . get DaysRent ed( ) ;i nt t hi sAmt = 0;

swi t ch ( aMovie.getPriceCode()) {case Movi e. REGULAR:

t hi sAmt = 2;i f ( days > 2)

t hi sAmt += ( days- 2) * 1. 5;br eak ;

case Movi e. NEW:

t hi sAmt = days* 3;br eak ;

case Movi e. CHI LDREN:t hi sAmt = 1. 5;

i f ( days > 3)t hi sAmt += ( days- 3) * 1. 5;

br eak ;}

r et ur n t hi sAmt ;}

8

©Robert B. France 15

Why is Design 1 Bad?

• A large portion of the functionality is within the Customer class. Some of the functionality can be migrated to other classes that contain the relevant information.

• If a new type of movie is added to the system (e.g., FAVORITES) then the switch statement in the Customer class would have to be extended and recompiled. This is an undesirable coupling between the Customer and Movie class.

©Robert B. France 16

Design 2 Overview• Functionality distributed

– Rental is responsible for calculating its charges– Customer interface extended to include a

getTotalCharge() method that returns the sum of the charges for the customer.

– Solution requires 2 loops through a customer’s rentals; one to record the charges for each rental, the other to calculate total charge.

• Here is a compromise in which reuse is given greater weight than efficiency: by restricting getTotalCharge to calculating only the sum of charges it can be used by other functions that require just that and nothing more; but this means that two loops are needed when printing out statements. In design 3 I show the other approach in which efficiency outweighs reuse.

• Case statement problem not solved.

9

©Robert B. France 17

Design 2 Class Diagram

Customer

Rental Movie

makes

refers_to

_rentals

_movie

1

*

*

1

name: String

addRental()getName()statement()getTotalCharge()

daysRented:int

getDaysRented()getMovie()getCharge()

title:StringpriceCode:int

getPriceCode()setPriceCode()getTitle()

©Robert B. France 18

Design 2 Sequence Model

10

©Robert B. France 19

Design 2: Code for Rentalcl ass Rent al {

\ …

doubl e get Char ge( ) {

doubl e t hi sAmt = 0;

swi t ch ( _movi e. get Pr i ceCode( ) ) {

case Movi e. REGULAR:

t hi sAmt = 2;

i f ( days > 2)

t hi sAmt += ( days- 2) * 1. 5;

br eak;

case Movi e. NEW:

t hi sAmt = days * 3;

br eak;

case Movi e. CHI LDREN:

t hi sAmt = 1. 5;

i f ( days > 3)

t hi sAmt += ( days- 3) * 1. 5;

br eak;

}

r et ur n t hi sAmt ;

}

©Robert B. France 20

Design 2: statement()publ i c St r i ng St at ement ( ) {

Enumer at i on r ent al s = _r ent al s. el ement s( ) ;

St r i ng r esul t = “ Recor d f or ” +get Name( ) +” \ n” ;

whi l e( r ent al s. hasMor eEl ement s( ) ) {

Rent al aRent al =( Rent al ) r ent al s. next El ement ( ) ;

doubl e t hi sAmt = aRent al . get Char ge( ) ;

r esul t += ” \ t ” +aRent al . get Movi e( ) . get Ti t l e( ) +

St r i ng. val ueOf ( t hi sAmt ) +” \ n” ;

}

r esul t +=” Amount owed i s” +St r i ng. val ueOf ( get Tot al Char ge( ) ) +” \ n” ;

r et ur n r esul t ;

}

11

©Robert B. France 21

Design 2: getTotalCharge()Pr i vat e doubl e get Tot al Char ge ( ) {

doubl e r esul t = 0;

Enumer at i on r ent al s = _r ent al s. el ement s( ) ;

whi l e( r ent al s. hasMor eEl ement s( ) ) {

Rent al aRent al =

( Rent al ) r ent al s. next El ement ( ) ;

r esul t += each. get Char ge( ) ;

}

r et ur n r esul t ;

}

©Robert B. France 22

Design 3: Notes• One may be tempted to subclass the Movie class but note

that a Movie object can be a New Release, then a Regular movie over a period of time (thus the specialization is not static). Better to have the price determinant part of the Movie as a separate abstract class (Price) that is subclassed; a Movie object can then have a reference to the specific Price subclass object (New, Regular, Children).– This is an application of the Gang of 4 State pattern.

• In this design getTotalCharge calculates more than just the total charge, it also documents the charges for each rental in the String object result. – Good aspect: program accesses rentals in one loop, unlike design 2

in which rentals are traversed twice.– Bad aspect: If only a total is needed the program does unnecessary

work w.r.t. documenting individual rentals.

12

©Robert B. France 23

Design 3: Class Diagram

Rental Movie

makes

refers_to

_rentals

_movie

1

*

*

1

Customer

name: String

addRental()getName()statement()getTotalCharge()

daysRented:int

getDaysRented()getMovie()getCharge()

title:String

getCharge()getTitle()

Price

getCharge(days)

NewRelPrice

getCharge(days)

ChildrenPrice

getCharge(days)

RegularPrice

getCharge(days)

1

*

has_state

©Robert B. France 24

Design 3: Sequence Model

13

©Robert B. France 25

Design 3: Communication Model

aCustomer:Customer

aRental:Rental aMovie:Movie

statement()

1.1:* [for each rental]: ch:=getCharge()

1:getTotalCharge()

1.1.1: ch:=getCharge(days)

aPrice:Price

1.1.1.1: ch:=getCharge(days)

1.1.2:t:=getTitle(){ transient}

©Robert B. France 26

Price and Subclasses Codecl ass Pr i ce{

\ …

abst r act doubl e get Char ge( i nt day sRent ed) ;

}

c l ass Regul ar Pr i ce ext ends Pr i ce {

doubl e get Char ge( i nt day sRent ed) {

doubl e r esul t = 2;

i f ( daysr ent ed > 2)

r esul t += ( day sRent ed- 2) * 1. 5;

r et ur n r esul t ;

}

c l ass NewRel Pr i ce ext ends Pr i ce {

doubl e get Char ge( i nt day sRent ed) {

r et ur n daysRent ed* 3;

}

c l ass Chi l dr enPr i ce ext ends Pr i ce {

doubl e get Char ge( i nt day sRent ed) {

r esul t = 1. 5;

i f ( daysRent ed > 3)

r esul t += ( day sRent ed- 3) * 1. 5;

r et ur n r esul t ;

}

14

©Robert B. France 27

Movie and Rental Codecl ass Movi e{

\ …

doubl e get Char ge( i nt daysRent ed) {

r et ur n _pr i ce. get Char ge( daysRent ed) ;

}

c l ass Rent al {

\ …

doubl e get Char ge( ) {

r et ur n _movi e. get Char ge( _daysRent ed) ;

}

©Robert B. France 28

statement() Codepubl i c St r i ng St at ement ( ) {

St r i ng r esul t = “ Recor d

f or ” +get Name( ) +” \ n” ;

doubl e t ot al Amt = get Tot al Char ge( r esul t ) ;

r esul t +=” Amount owed i s”

+St r i ng. val ueOf ( t ot al Amt ) +” \ n” ;

r et ur n r esul t ;

}

15

©Robert B. France 29

getTotalCharge() Codepubl i c doubl e get Tot al Char ge ( St r i ng r esul t ) {

Enumer at i on r ent al s = _r ent al s. el ement s( ) ;

doubl e t ot al Amt = 0;

whi l e( r ent al s. hasMor eEl ement s( ) ) {

Rent al aRent al =( Rent al ) r ent al s. next El ement ( ) ;

doubl e t hi sAmt = aRent al . get Char ge( ) ;

r esul t += ” \ t ” +aRent al . get Movi e( ) . get Ti t l e( ) +

St r i ng. val ueOf ( t hi sAmt ) +” \ n” ;

t ot al Amt += t hi sAmt

}

r et ur n t ot al Amt ;

}