Presentation is loading. Please wait.

Presentation is loading. Please wait.

Refactoring II Dealing with Polymorphism. Switch in Rental Switches on Movie! class Rental … public double getCharge() { double result = 0; switch (getMovie().getPriceCode()){

Similar presentations


Presentation on theme: "Refactoring II Dealing with Polymorphism. Switch in Rental Switches on Movie! class Rental … public double getCharge() { double result = 0; switch (getMovie().getPriceCode()){"— Presentation transcript:

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


Download ppt "Refactoring II Dealing with Polymorphism. Switch in Rental Switches on Movie! class Rental … public double getCharge() { double result = 0; switch (getMovie().getPriceCode()){"

Similar presentations


Ads by Google