CS 635 Advanced Object-Oriented Design & Programming Spring Semester, 2001 Refactoring with Patterns |
||
---|---|---|
© 2001, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 10-May-01 |
Some Refactorings with Patterns
But first some common refactorings
Replace Nested Conditional with Guard Clauses (250)[1]
You have a method with nested conditionals
The method is hard to read
The method does not make clear the normal path of execution
So
Use guard clauses for all the special cases
Example
payAmount() { double result; if (_isDead ) result = deadAmount(); else { if (_isSeparated ) result = separatedAmount(); else { if (_isRetired ) result = retiredAmount(); else result = self normalPayAmount(); } } return result; }
becomes
payAmount() { if (_isDead ) return deadAmount(); if (_isSeparated ) return separatedAmount(); if (_isRetired ) return retiredAmount(); return normalPayAmount(); }
Replace Conditional with Polymorphism (255)[2]
You have case statements or ifs
The cases depend on types
Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract
Example
class Employee { public double payAmount() if (_type = Employee.ENGINEER) return _monthlySalary; if (_type = Employee.MANAGER) return _monthlySalary * 2; if (_type = Employee.ENGINEER) return _monthlySalary/2; }
So
class Employee { public double payAmount() { return _type.payAmount(this); } }
abstract class EmployeeType { pubic abstract double payAmount(Employee x); }
class Engineer extends EmployeeType { pubic double payAmount(Employee anEmployee) { return anEmployee.monthlySalary(); } }
class Manager extends EmployeeType { pubic double payAmount(Employee anEmployee) { return anEmployee.monthlySalary() * 2; } }
class Instructor extends EmployeeType { pubic double payAmount(Employee anEmployee) { return anEmployee.monthlySalary() / 2; } }
Replace Type Code with State/Strategy (227) [3]
You have a type code that affects the behavior of a class, but you can not use subclassing
So
Replace the type code with a state object
Example
See example for Replace Conditional with Polymorphism
Form Template Method (345) [4]
We have two methods in subclasss that perform similar steps in the same order
So:
Example
We start with:
class ResidentialSite extends Site { public double getBillableAmount() { double base = _units * _rate; double tax = base * Site.TAX_RATE; return base * tax; } }
class LifelineSite extends Site { public double getBillableAmount() { double base = _units * _rate * 0.5; double tax = base * Site.TAX_RATE * 0.3; return base * tax; } }
We refactor to:
abstract class Site { public double getBillableAmount() { return getBaseAmount() + getTaxAmount(); } public abstract double getBaseAmount(); public abstract double getTaxAmount(); } class ResidentialSite extends Site { public double getBaseAmount() { return _units * _rate; } public double getTaxAmount() { return getBaseAmount() * Site.TAX_RATE; } } class LifelineSite extends Site { public double getBaseAmount() { return _units * _rate * 0.5; } public double getTaxAmount() { return getBaseAmount() * Site.TAX_RATE * 0.3; }
Replace Constructor with Factory Method (304) [5]
You want to do more than simple construction when you create an object
So
Replace the constructor with a factory method
One Motivation
You want to create an object based on a type code
Different type codes require different types of objects from subclasses
A constructor can only return one type
So use a factory method
The factory method
Encapsulate Constructors with Factory Methods[6]
You have a class with lots of constructors
Constructors do not communicate intent well
Many constructors on a class make it hard to decide which constructor to use
So
Encapsulate the constructors with intention-revealing factory methods
Mechanics
Identify the class that has many constructors
Identify the catchall constructor
Example
Loan Constructors
public Loan(notional, outstanding, customerRating, expire)
public Loan(notional, outstanding, customerRating, expire, maturity)
public Loan(capitalStategy, outstanding, customerRating, expire, maturity)
public Loan(type, capitalStategy, outstanding, customerRating, expire)
public Loan(type, capitalStategy, outstanding, customerRating, expire, maturity)
After refactoring:
protected Loan(type, capitalStategy, outstanding, customerRating, expire, maturity)
static Loan newTermLoan(notional, outstanding, customerRating, expire)
static Loan newTermLoanWithStategy(capitalStategy, notional, outstanding, customerRating, expire)
static Loan newRevolver(notional, outstanding, customerRating, expire)
static Loan newRevolverWithStategy(capitalStategy, notional, outstanding, customerRating, expire)
static Loan newRCTL(notional, outstanding, customerRating, expire, maturity)
static Loan newRCTLWithStategy(capitalStategy, notional, outstanding, customerRating, expire, maturity)
Encapsulate Subclass Constructors with Superclass Factory Methods[7]
You have a number of subclasses that implement a common interface
You want client code to interact with subclass instances through the common interface
Subclass instances are created from subclass constructors
This allows clients to know the subclass type
This leads programmers to add new features to subclasses, not the common interface
So
Encapsulate subclass constructors with intention-revealing factory methods in the parent class
Mechanics
Make the subclass constructor protected
Create an intention-revealing Factory Method in the superclass
Extract Special-Case Logic into Decorators[8]
Your classes/methods have lots of special case logic
Most of the time the normal case is used
There are lots of special cases
The special cases make the code hard to read & maintain
So
Keep only normal case logic in the class and extract the special case logic into decorators
Example
Class CarRental { public double calculatePrice() { double price = _model.price() * _days; if (_hasInsurance) price += insuranceAmount(); if (_neededRefuelOnReturn) price += refuelPrice(); return price; } }
Refactored Classes
Decorator Code
class Insurance extends CarRentalDecorator { public double calculatePrice() { return rental.calculatePrice() + insuranceAmount(); } private double insuranceAmount() { blah } } class RefuelOnReturn extends CarRentalDecorator { public double calculatePrice() { return rental.calculatePrice() + refuelPrice(); } private double refuelPrice() { blah } }
[1] Fowler 99, pp. 250-254
[2] Fowler 99 pp. 255-259
[3] Fowler 99, pp. 227-231
[4] Fowler 99
[5] Fowler 99
[6] Kerievsky 2000, pp. 4-8
[7] Kerievsky 2000, pp. 9-12
[8] Kerievsky 2000, pp. 21-29
Copyright ©, All rights reserved.
2001 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.
Previous    visitors since 10-May-01