Download presentation
Presentation is loading. Please wait.
Published byRaymond Lane Modified over 9 years ago
1
Refactoring II Dealing with Polymorphism
2
Switch in Rental Switches on 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; }
3
So Put It In Movie class 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; }
4
Remarks This needs data from Rental This needs data from Rental So make it an int parameter daysRented instead of a call getDaysRented() So make it an int parameter daysRented instead of a call getDaysRented() Leave the type of Movie alone for now Leave the type of Movie alone for now A good candidate for polymorphism This type of information is more volatile, i.e. subject to change
5
Change getCharge on Rental class Rental … public double getCharge(){ return _movie.getCharge(_daysRented): }
6
Similar for getFrequentRenterPoints() Move it from Rental to Movie Move 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;
7
Problem Movie data used in switch statement Movie data used in switch statement Fix it with inheritance and polymorphism Fix it with inheritance and polymorphism Not possible since child type can change over the movie class’s lifetime Not possible since child type can change over the movie class’s lifetime So, use state/strategy pattern So, use state/strategy pattern
8
The State Pattern ContextState ConcreteStateAConcreteStateB state.handle() handle() request
9
State Pattern Behavior is encapsulated in the concrete state objects Behavior is encapsulated in the concrete state objects Context delegates to one of the states Context delegates to one of the states It appears that the state class is changing It appears that the state class is changing But in reality it is just changing its behavior Change behavior by composing with a different object Change behavior by composing with a different object
10
State Pattern Context: The class with the internal states Context: The class with the internal states State: The common interface for all concrete states State: The common interface for all concrete states ConcreteStates: deal with requests from the context. Each provides own implementation for the request ConcreteStates: deal with requests from the context. Each provides own implementation for the request When Context changes state so will the concrete state’s behavior When Context changes state so will the concrete state’s behavior
11
We change
12
To Replace Type Code with State/Strategy
13
Step 1: ensure fields accessed by get and set Work on constructor in movie Work on constructor in movie Use Self Encapsulate Field Use Self Encapsulate Field class Movie{ … public Movie (String name, int priceCode){ _title = name; setPriceCode(priceCode);} } Self Encapsulate Field
14
Why encapsulate data access? If a datum is accessed in a base class, but you want to compute a different value from this in a child. If a datum is accessed in a base class, but you want to compute a different value from this in a child. If this is encapsulated, you can override the accessors to change the value. If this is encapsulated, you can override the accessors to change the value. See Self Encapsulate Field See Self Encapsulate Field
15
Step 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 ;} }
16
Step 3: Change get and set in Movie class Change the accessors Change 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; } … }
17
Step 4 Move getCharge to Price 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
18
Don’t forget you need it in Movie class Movie{… double getCharge(int daysRented){ return _price.getCharge(daysRented); } … }
19
Get rid of the switch
20
Do a getCharge() in class RegularPrice class RegularPrice{… double getCharge(int daysRented){ double result = 2.0; if (daysRented>2) result += (daysRented-2)*1.5; return result; } … }
21
Don’t forget to compile and test Replace Conditional With Polymorphism
22
Now same for ChildrensPrice class ChildrensPrice{… double getCharge(int daysRented){ double result = 1.5; if (daysRented > 3) result += (daysRented-3)*1.5; return result; } … }
23
And finally NewReleasePrice class NewReleasePrice{… double getCharge(int daysRented){ return daysRented*3; } … }
24
Finally get rid of the old getCharge class Price{… abstract double getCharge(int daysRented); … }
25
Do the same to getFrequentRenterPoints 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; }
26
Now Override to NewReleasePrice 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; } … }
27
When do I refactor? “If it stinks change it”
28
Hence the Name: Code Smelling
29
Code Smells Duplicated Code Duplicated Code Long Method Long Method Large Class Large Class Long Parameter List Long Parameter List Divergent Change Divergent Change Shotgun Surgery Shotgun Surgery
30
More Code Smells Feature Envy Feature Envy Data Clumps Data Clumps Primitive Obsession Primitive Obsession Switch Statement Switch Statement Parallel Inheritance Hierarchy Parallel Inheritance Hierarchy Lazy Class Lazy Class
31
And Even More Speculative Generality Speculative Generality Temporary Field Temporary Field Message Chains Message Chains Middle Man Middle Man Inappropriate Intimacy Inappropriate Intimacy Incomplete Library Class Incomplete Library Class
32
And Finally Data Class Data Class Comments Comments Refused Bequest Refused Bequest And Many More And Many More
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.