refactoring ii dealing with polymorphism. switch in rental switches on movie! class rental …...
TRANSCRIPT
Refactoring IIRefactoring II
Dealing with PolymorphismDealing with Polymorphism
Switch in Rental Switches on Switch in Rental Switches on Movie!Movie!
class Rental …public double getCharge(){double result = 0;switch (getMovie().getPriceCode()){ case Movie.REGULAR: result += 2; if (getDaysRented()>2) result += (getDaysRented()-2)*1.5; break; case Movie.NEWRELEASE: result += getDaysRented()*3; break; case Movie.CHILDRENS: result += 1.5; if (getDaysRented()-3)*1.5; break; }return result;}
So Put It In MovieSo Put It In Movieclass Movie …public double getCharge(int daysRented){double result = 0;switch (getPriceCode(){ case Movie.REGULAR: result += 2; if (getDaysRented()>2) result += (getDaysRented()-2)*1.5; break; case Movie.NEWRELEASE: result += getDaysRented()*3; break; case Movie.CHILDRENS: result += 1.5; if (getDaysRented()-3)*1.5; break; }return result;}
RemarksRemarks
• This needs data from RentalThis needs data from Rental• So make it an int parameter daysRented So make it an int parameter daysRented
instead of a call getDaysRented()instead of a call getDaysRented()• Leave the type of Movie alone for nowLeave the type of Movie alone for now
A good candidate for polymorphismA good candidate for polymorphism This type of information is more volatile, i.e. This type of information is more volatile, i.e.
subject to changesubject to change
Change getCharge on RentalChange getCharge on Rental
class Rental …public double getCharge(){ return _movie.getCharge(_daysRented):}
Similar for Similar for getFrequentRenterPoints()getFrequentRenterPoints()
• Move it from Rental to MovieMove it from Rental to Movie
• Details not shown (exercise)Details not shown (exercise)
class Rental … int getFrequentRenterPoints(){ if((getMovie().getPriceCode()== Movie.NEW_RELEASE) && getDaysRented() > 1) return 2 else return 1;
ProblemProblem
• Movie data used in switch statementMovie data used in switch statement• Fix it with inheritance and polymorphismFix it with inheritance and polymorphism• Not possible since child type can change Not possible since child type can change
over the movie class’s lifetimeover the movie class’s lifetime• So, use state/strategy patternSo, use state/strategy pattern
The State PatternThe State Pattern
Context State
ConcreteStateA ConcreteStateB
state.handle()
handle()
handle()handle()
request
State PatternState Pattern
• Behavior is encapsulated in the concrete Behavior is encapsulated in the concrete state objectsstate objects
• Context delegates to one of the statesContext delegates to one of the states• It appears that the state class is changingIt appears that the state class is changing
But in reality it is just changing its behaviorBut in reality it is just changing its behavior
• Change behavior by composing with a Change behavior by composing with a different objectdifferent object
State PatternState Pattern
• ContextContext: The class with the internal states: The class with the internal states• StateState: The common interface for all : The common interface for all
concrete statesconcrete states• ConcreteStatesConcreteStates: deal with requests from : deal with requests from
the context. Each provides own the context. Each provides own implementation for the requestimplementation for the request
• When Context changes state so will the When Context changes state so will the concrete state’s behaviorconcrete state’s behavior
We changeWe change
ToTo
Replace Type Code with State/Strategy
Step 1: ensure fields accessed Step 1: ensure fields accessed by by getget and and setset
• Work on constructor in Work on constructor in moviemovie• Use Self Use Self Encapsulate FieldEncapsulate Field
class Movie{ …public Movie (String name, int priceCode){ _title = name; setPriceCode(priceCode);}
}
Self Encapsulate Field
Why encapsulate data access?Why encapsulate data access?
• If a datum is accessed in a base class, but If a datum is accessed in a base class, but you want to compute a different value from you want to compute a different value from this in a child.this in a child.
• If this is encapsulated, you can override the If this is encapsulated, you can override the accessors to change the value.accessors to change the value.
• See See Self Encapsulate FieldSelf Encapsulate Field
Step 2: Add new classesStep 2: Add new classes
abstract class Price{ abstract int getPriceCode();}class ChildrensPrice extends Price{ public int getPriceCode(){return Movie.CHILDRENS;}}class NewReleasePrice extends Price{ public int getPriceCode(){return Movie.NEW_RELEASE;}}Class RegularPrice extends Price{ public int getPriceCode(){return Movie.REGULAR;}}
Step 3: Change Step 3: Change getget and and setset in in Movie classMovie class
• Change the accessorsChange the accessors
class Movie{…private Price _price;public int getPriceCode(){return _price.getPriceCode;}public void setPriceCode(int arg){ switch(arg){ case REGULAR: _price = new RegularPrice(); break; case CHILDRENS: _price = new ChildrensPrice();break; case NEW_RELEASE: _price = new NewReleasePrice(); break; }…}
Step 4 Move Step 4 Move getChargegetCharge to to PricePrice
class Price{…public double getCharge(int daysRented){ double result = 0.0; switch (getPriceCode()){ case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented–2)*1.5; break; case Movie.NEW_RELEASE: result += daysRented*3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented>3) result += (daysRented-3)*1.5; break; } return result;}
Move Method
Don’t forget you need it in Don’t forget you need it in MovieMovie
class Movie{… double getCharge(int daysRented){ return _price.getCharge(daysRented); }…}
Get rid of the switchGet rid of the switch
Do a Do a getCharge()getCharge() in class in class RegularPriceRegularPrice
class RegularPrice{… double getCharge(int daysRented){ double result = 2.0; if (daysRented>2) result += (daysRented-2)*1.5; return result; }…}
Don’t forget to compile and testDon’t forget to compile and test
Replace Conditional With Polymorphism
Now same for Now same for ChildrensPriceChildrensPrice
class ChildrensPrice{… double getCharge(int daysRented){ double result = 1.5; if (daysRented > 3) result += (daysRented-3)*1.5; return result; }…}
And finally And finally NewReleasePriceNewReleasePrice
class NewReleasePrice{… double getCharge(int daysRented){ return daysRented*3; }…}
Finally get rid of the old Finally get rid of the old getChargegetCharge
class Price{… abstract double getCharge(int daysRented);…}
Do the same to Do the same to getFrequentRenterPointsgetFrequentRenterPoints
class Movie{… int getFrequentRenterPoints(int daysRented){ return _price.getFrequentRenterPoints(daysRented);…}
Class Price{… int getFrequentRenterPoints(int daysRented){ if ((getPriceCode()==Movie.NEW_RELEASE)&& daysRented >1) return 2; else return 1;}
Now Override to Now Override to NewReleasePriceNewReleasePrice
class NewReleasePrice{… int getFrequentRenterPoints(int daysRented){ return (daysRented>1)?2:1;}
And keep a defined method on superclass Price
Class Price{… int getFrequentRenterPoints(int daysRented){ return 1; }…}
When do I refactor?When do I refactor?
““If it stinks change it”If it stinks change it”
Hence the Name: Code SmellingHence the Name: Code Smelling
Code SmellsCode Smells
• Duplicated CodeDuplicated Code• Long MethodLong Method• Large ClassLarge Class• Long Parameter ListLong Parameter List• Divergent ChangeDivergent Change• Shotgun SurgeryShotgun Surgery
More Code SmellsMore Code Smells
• Feature EnvyFeature Envy• Data ClumpsData Clumps• Primitive ObsessionPrimitive Obsession• Switch StatementSwitch Statement• Parallel Inheritance HierarchyParallel Inheritance Hierarchy• Lazy ClassLazy Class
And Even MoreAnd Even More
• Speculative GeneralitySpeculative Generality• Temporary FieldTemporary Field• Message ChainsMessage Chains• Middle ManMiddle Man• Inappropriate IntimacyInappropriate Intimacy• Incomplete Library ClassIncomplete Library Class
And FinallyAnd Finally
• Data ClassData Class• CommentsComments• Refused BequestRefused Bequest• And Many MoreAnd Many More