Refactoring: How To Improve Your Code
Refactoring: How To Improve Your Code
ARTEM TABALIN
What is refactoring?
Definition. Goals. When to Refactor. Pitfalls.
What is Refactoring?
•ꢀ Code reviews
Problems with Refactoring
Database
•ꢀ difficult to change since tightly coupled to scheme
•ꢀ existing data migration
Changing Interface
•ꢀ deprecate old interface
•ꢀ just call new method in old method
•ꢀ don’t publish premature interfaces
When You Shouldn’t Refactor
•ꢀ Sections in method
•ꢀ To many methods
•ꢀ To many fields
Extract Class / Extract Subclass
Long Parameter List
Exceptions
•ꢀ Strategy
•ꢀ Visitor
•ꢀ Data Access Layer
Data Clumps
ꢀ
Speculative Generality
•ꢀ Unnecessary delegation
Inline Class
•ꢀ Lazy Class
Inline Class
•ꢀ Temporary Field
Extract Class
•ꢀ Refused Bequest
Replace Inheritance with Delegation
Comments
Used like a deodorant
When
•ꢀ Method is too long
•ꢀ Code needs an explanatory comment
Why
•ꢀ Increase reusability
•ꢀ Higher level methods become more readable
•ꢀ Easier to override
Extract Method
publicꢀvoidꢀPrintClient(Clientꢀclient)ꢀ{ꢀ
ꢀꢀꢀꢀPrintBanner();ꢀ
ꢀꢀꢀꢀ//ꢀprintꢀclientꢀdetailsꢀ
ꢀꢀꢀꢀConsole.Write("Name:ꢀ"ꢀ+ꢀclient.Name);ꢀ
ꢀꢀꢀꢀConsole.Write("Address:ꢀ"ꢀ+ꢀclient.Address);ꢀ
}ꢀ
publicꢀvoidꢀPrintClient(Clientꢀclient)ꢀ{ꢀ
ꢀꢀꢀꢀPrintBanner();ꢀ
ꢀꢀꢀꢀPrintClientDetails(client);ꢀ
}ꢀ
privateꢀvoidꢀPrintClientDetails(Clientꢀclient)ꢀ{ꢀ
ꢀꢀꢀꢀConsole.Write("Name:ꢀ"ꢀ+ꢀclient.Name);ꢀ
ꢀꢀꢀꢀConsole.Write("Address:ꢀ"ꢀ+ꢀclient.Address);ꢀ
}ꢀ
Inline Method
Put the method body to callers and remove it
When
•ꢀ Method body is as clear as its name
•ꢀ Calling method is badly factored
Why
•ꢀ Reduce redundant delegation
•ꢀ Restructure calling method
Inline Method
publicꢀintꢀGetRating()ꢀ{ꢀ
ꢀꢀꢀꢀreturnꢀMoreThanFiveLateDeliveries()ꢀ?ꢀ2ꢀ:ꢀ1;ꢀ
}ꢀ
privateꢀboolꢀMoreThanFiveLateDeliveries()ꢀ{ꢀ
ꢀꢀꢀꢀreturnꢀ_numberOfLateDeliveriesꢀ>ꢀ5;ꢀ
}ꢀ
publicꢀintꢀGetRating()ꢀ{ꢀ
ꢀꢀꢀꢀreturnꢀ(_numberOfLateDeliveriesꢀ>ꢀ5)ꢀ?ꢀ2ꢀ:ꢀ1;ꢀ
}ꢀ
Replace Temp with Query
Extract expression to a method and replace temp
When
•ꢀ Temp holds an expression result for other methods
•ꢀ Temp prevents from another refactoring
Why
•ꢀ Cleaner code
•ꢀ Shorter method
•ꢀ The result is available to all methods
Replace Temp with Query
publicꢀdoubleꢀGetPrice()ꢀ{ꢀ
ꢀꢀꢀꢀdoubleꢀbasePriceꢀ=ꢀ_quantityꢀ*ꢀ_itemPrice;ꢀ
ꢀ
ꢀꢀꢀꢀreturnꢀ(basePriceꢀ>ꢀ1000)ꢀꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀ?ꢀbasePriceꢀ*ꢀ0.95ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀ:ꢀbasePriceꢀ*ꢀ0.98;ꢀ
}ꢀ
publicꢀdoubleꢀGetPrice()ꢀ{ꢀ
ꢀꢀꢀꢀreturnꢀ(BasePriceꢀ>ꢀ1000)ꢀꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀ?ꢀBasePriceꢀ*ꢀ0.95ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀ:ꢀBasePriceꢀ*ꢀ0.98;ꢀ
}ꢀ
ꢀ
privateꢀdoubleꢀBasePriceꢀ{ꢀ
ꢀꢀꢀꢀgetꢀ{ꢀreturnꢀ_quantityꢀ*ꢀ_itemPrice;ꢀ}ꢀ
}ꢀ
Introduce Explaining Variable
Assign a part of expression to explaining variable
When
•ꢀ Complex condition
•ꢀ Complex algorithm
Why
•ꢀ Improve code readability
Introduce Explaining Variable
ifꢀ((platform.toUpperCase().indexOf("MAC")ꢀ>ꢀ-1)ꢀ&&ꢀ
ꢀꢀꢀꢀ(browser.toUpperCase().indexOf("IE")ꢀ>ꢀ-1)ꢀ&&ꢀ
ꢀꢀꢀꢀisInitialized()ꢀ&&ꢀresizeꢀ>ꢀ0)ꢀ{ꢀ
ꢀꢀꢀꢀ//ꢀdoꢀsomethingꢀ
}ꢀ
varꢀisMacꢀ=ꢀplatform.toUpperCase().indexOf("MAC")ꢀ>ꢀ-1;ꢀ
varꢀisIEBrowserꢀ=ꢀbrowser.toUpperCase().indexOf("IE")ꢀ>ꢀ-1;ꢀ
varꢀisResizedꢀ=ꢀresizeꢀ>ꢀ0;ꢀ
ifꢀ(isMacꢀ&&ꢀisIEBrowserꢀ&&ꢀisInitialized()ꢀ&&ꢀisResized){ꢀ
ꢀꢀꢀꢀ//ꢀdoꢀsomethingꢀ
}ꢀ
Replace Method with Method Object
When
•ꢀ Long method with lots of temp variables
Why
•ꢀ Improve readability
•ꢀ Increase reusability
Replace Method with Method Object
classꢀTransactionꢀ{ꢀ
ꢀꢀꢀꢀpublicꢀdoubleꢀBankFee()ꢀ{ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀdoubleꢀamount;ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀdoubleꢀprice;ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀdoubleꢀtax;ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀ//ꢀlongꢀcomputationsꢀ
ꢀꢀꢀꢀ}ꢀ
BankFeeCalculator
Transaction -amount
1 -price
+BankFee() -tax
+Compute()
When
•ꢀ Method references another object too much
•ꢀ Two classes are too coupled
•ꢀ Class is overcomplicated
Why
•ꢀ Lower coupling
•ꢀ Simplify design
Move Method
classꢀAccountꢀ{ꢀ classꢀAccountꢀ{ꢀ
ꢀꢀprivateꢀAccountTypeꢀ_type;ꢀ ꢀꢀpublicꢀdoubleꢀBankCharge()ꢀ{ꢀ
ꢀꢀpublicꢀdoubleꢀBankCharge()ꢀ{ꢀ ꢀꢀꢀꢀdoubleꢀresultꢀ=ꢀFIXED_FEE;ꢀ
ꢀꢀꢀꢀdoubleꢀresultꢀ=ꢀFIXED_FEE;ꢀ ꢀꢀꢀꢀif(HasOverdraft())ꢀ
ꢀꢀꢀꢀif(HasOverdraft())ꢀ ꢀꢀꢀꢀꢀꢀresultꢀ+=ꢀ_type.OverdraftFee();ꢀ
ꢀꢀꢀꢀꢀꢀresultꢀ+=ꢀOverdraftFee();ꢀ ꢀꢀꢀꢀreturnꢀresult;ꢀ
ꢀꢀꢀꢀreturnꢀresult;ꢀ ꢀꢀ}ꢀ
ꢀꢀ}ꢀ
classꢀAccountTypeꢀ{ꢀ
ꢀꢀprivateꢀdoubleꢀOverdraftFee()ꢀ{ꢀ
ꢀꢀpublicꢀdoubleꢀOverdraftFee()ꢀ{ꢀ
ꢀꢀꢀꢀif(_type.IsPremium())ꢀ{ꢀ
ꢀꢀꢀꢀif(IsPremium())ꢀ{ꢀ
ꢀꢀꢀꢀꢀꢀ//ꢀpremiumꢀaccountꢀ
ꢀꢀꢀꢀꢀꢀ//ꢀpremiumꢀaccountꢀ
ꢀꢀꢀꢀ}ꢀelseꢀ{ꢀ
ꢀꢀꢀꢀ}ꢀelseꢀ{ꢀ
ꢀꢀꢀꢀꢀꢀ//ꢀstandardꢀaccountꢀ
ꢀꢀꢀꢀꢀꢀ//ꢀstandardꢀaccountꢀ
ꢀꢀꢀꢀ}ꢀ
ꢀꢀꢀꢀ}ꢀ
ꢀꢀ}ꢀ
ꢀ ꢀꢀ}ꢀ
Move Field
Move the field to class that uses it most
When
•ꢀ Another class uses a field more than its owner
•ꢀ Performing Extract Class refactoring
Why
•ꢀ Lower coupling
•ꢀ Simplify design
Move Field
ꢀ ꢀ
classꢀAccountꢀ{ꢀ classꢀAccountꢀ{ꢀ
ꢀꢀprivateꢀAccountTypeꢀ_type;ꢀ ꢀꢀdoubleꢀInterest(intꢀdays)ꢀ{ꢀ
ꢀꢀꢀꢀreturnꢀ_type.InterestRateꢀ*ꢀdays/365;ꢀ
ꢀꢀprivateꢀdoubleꢀ_interestRate;ꢀ ꢀꢀ}ꢀ
ꢀꢀpublicꢀdoubleꢀInterestRateꢀ{ꢀ
ꢀꢀꢀꢀgetꢀ{ꢀreturnꢀ_interestRate;ꢀ}ꢀ classꢀAccountTypeꢀ{ꢀ
ꢀꢀ}ꢀ ꢀꢀprivateꢀdoubleꢀ_interestRate;ꢀ
ꢀꢀdoubleꢀInterest(intꢀdays)ꢀ{ꢀ ꢀꢀpublicꢀdoubleꢀInterestRateꢀ{ꢀ
ꢀꢀꢀꢀreturnꢀInterestRateꢀ*ꢀdays/365;ꢀ ꢀꢀꢀꢀgetꢀ{ꢀreturnꢀ_interestRate;ꢀ}ꢀ
ꢀꢀ}ꢀ ꢀꢀ}ꢀ
ꢀ
Extract Class
Create new class and move fields and methods
When
•ꢀ Class is too big
•ꢀ Single Responsibility Principle violation
•ꢀ Data or methods dependent on each other
Why
•ꢀ Simplify design
Extract Class
Person
-name
-officeAreaCode
-officeNumber
+OfficePhone()
Person PhoneNumber
-officePhone
areaCode
-name
-number
+OfficePhone()
+Phone()
Inline Class
Move fields & methods from class and remove it
When
•ꢀ Class is useless
Why
•ꢀ Simplify design
Inline Class
Person PhoneNumber
-phone
-areaCode
-name
-number
+Phone()
+Phone()
Person
-name
-areaCode
-number
+Phone()
Hide Delegate
Create method on the server to hide the delegate
When
•ꢀ object.getAnotherObject().method()
Why
•ꢀ Lower coupling
•ꢀ Strengthen encapsulation
Hide Delegate
Client
Client
Person
+Manager()
Person Department
+Department() +Manager()
Department
Organizing Data
Self Encapsulate Field. Replacing Value with Object.
Replace Magic Number with Constant.
Replace Type Code with Subclasses/State/Strategy.
Self Encapsulate Field
Create and use getter and setter to access field
When
•ꢀ Provide access to the field from outside
•ꢀ Override property in a child class
Why
•ꢀ Strengthen encapsulation
•ꢀ Higher flexibility
Self Encapsulate Field
privateꢀdoubleꢀ_interestRate;ꢀ
publicꢀdoubleꢀInterest(intꢀdays)ꢀ{ꢀ
ꢀꢀreturnꢀ_interestRateꢀ*ꢀdays/365;ꢀ
}ꢀ
privateꢀdoubleꢀ_interestRate;ꢀꢀꢀ
publicꢀdoubleꢀInterestRateꢀ{ꢀ
ꢀꢀgetꢀ{ꢀreturnꢀ_interestRate;ꢀ}ꢀ
ꢀꢀsetꢀ{ꢀ_interestRateꢀ=ꢀvalue;ꢀ}ꢀ
}ꢀ
publicꢀdoubleꢀInterest(intꢀdays)ꢀ{ꢀ
ꢀꢀreturnꢀInterestRateꢀ*ꢀdays/365;ꢀ
}ꢀꢀꢀ
ꢀ
Replace Data Value with Object
Turn the data item to an object
When
•ꢀ Data item has dedicated methods
•ꢀ Data items are used together in lots of places
Why
•ꢀ Higher flexibility
Replace Data Value with Object
Order
-customer: string
Customer
Order
-customer -name: string
Replace Magic Number with Constant
When
•ꢀ There is a magic value in the code
Why
•ꢀ Improve code readability
Replace Magic Number with Constant
doubleꢀPotentialEnergy(doubleꢀmass,ꢀdoubleꢀheight)ꢀ{ꢀ
ꢀꢀreturnꢀmassꢀ*ꢀ9.81ꢀ*ꢀheight;ꢀ
}ꢀ
staticꢀconstꢀdoubleꢀGRAVITATIONAL_CONSTANTꢀ=ꢀ9.81;ꢀ
doubleꢀPotentialEnergy(doubleꢀmass,ꢀdoubleꢀheight)ꢀ{ꢀ
ꢀꢀreturnꢀmassꢀ*ꢀGRAVITATIONAL_CONSTANTꢀ*ꢀheight;ꢀ
}ꢀ
ꢀ
Replace Type Code with Subclasses
When
•ꢀ There is a type code influencing on class behavior
Why
•ꢀ Increase extensibility
Replace Type Code with Subclasses
classꢀEmployeeꢀ{ꢀ abstractꢀclassꢀEmployeeꢀ{ꢀ
ꢀꢀabstractꢀEmployeeTypeꢀTypeꢀ{ꢀget;ꢀ}ꢀ
ꢀꢀprivateꢀEmployeeTypeꢀ_type;ꢀ
ꢀꢀabstractꢀdoubleꢀBonusꢀ{ꢀget;ꢀ}ꢀ
ꢀꢀEmployee(EmployeeTypeꢀtype)ꢀ{ꢀ }ꢀ
ꢀꢀꢀꢀ_typeꢀ=ꢀtype;ꢀ
ꢀꢀ}ꢀ classꢀEngineer:ꢀEmployeeꢀ{ꢀ
ꢀꢀpublicꢀoverrideꢀEmployeeTypeꢀTypeꢀ{ꢀ
ꢀꢀpublicꢀdoubleꢀBonusꢀ{ꢀ
ꢀꢀꢀꢀgetꢀ{ꢀ ꢀꢀꢀꢀgetꢀ{ꢀꢀ
ꢀꢀꢀꢀꢀꢀreturnꢀEmployeeType.Engineer;ꢀꢀ
ꢀꢀꢀꢀꢀꢀswitch(_type)ꢀ{ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀcaseꢀEmployeeType.Engineer:ꢀ ꢀꢀꢀꢀ}ꢀ
ꢀꢀ}ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀꢀꢀreturnꢀ0.0;ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀcaseꢀEmployeeType.Salesman:ꢀ ꢀꢀpublicꢀoverrideꢀdoubleꢀBonusꢀ{ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀꢀꢀreturnꢀ0.25;ꢀ ꢀꢀꢀꢀgetꢀ{ꢀꢀ
ꢀꢀꢀꢀꢀꢀ}ꢀ ꢀꢀꢀꢀꢀꢀreturnꢀ0.0;ꢀꢀ
ꢀꢀꢀꢀ}ꢀ ꢀꢀꢀꢀ}ꢀ
ꢀꢀ}ꢀ ꢀꢀ}ꢀ
} }ꢀ
Replace Type Code with State/Strategy
When
•ꢀ There is a type code influencing on class behavior
•ꢀ Type code may change during object life
Why
•ꢀ Increase extensibility
Replace Type Code with State/Strategy
-type
Employee EmployeeType
Salesman Engineer
Simplifying Conditional Expressions
Decompose Conditional Expression.
Replace Conditional with Polymorphism.
Guard Clauses.
Decompose Conditional
Extract methods from condition and branches
When
•ꢀ Condition with complex expression and branches
Why
•ꢀ Improve code readability
Decompose Conditional
ifꢀ(date.Before(SUMMER_START)ꢀ||ꢀdate.After(SUMMER_END))ꢀ
ꢀꢀchargeꢀ=ꢀquantityꢀ*ꢀ_winterRateꢀ+ꢀ_winterServiceCharge;ꢀ
elseꢀ
ꢀꢀchargeꢀ=ꢀquantityꢀ*ꢀ_summerRate;ꢀ
ifꢀ(NotSummer(date))ꢀ
ꢀꢀchargeꢀ=ꢀWinterCharge(quantity);ꢀ
elseꢀꢀ
ꢀꢀchargeꢀ=ꢀSummerCharge(quantity);ꢀ
Replace Nested Conditional with Guard Clauses
When
•ꢀ Conditional expression with special case branch
Why
•ꢀ Improve code readability
Replace Nested Conditional with Guard Clauses
doubleꢀPayAmount()ꢀ{ꢀ doubleꢀPayAmount()ꢀ{ꢀ
ꢀꢀdoubleꢀresult;ꢀ
ꢀꢀif(_isDead)ꢀꢀ
ꢀꢀif(_isDead)ꢀ ꢀꢀꢀꢀreturnꢀDeadAmount();ꢀ
ꢀꢀꢀꢀresultꢀ=ꢀDeadAmount();ꢀ
ꢀꢀif(_isSeparated)ꢀꢀ
ꢀꢀelseꢀ{ꢀ
ꢀꢀꢀꢀif(_isSeparated)ꢀꢀ ꢀꢀꢀꢀreturnꢀSeparatedAmount();ꢀ
ꢀꢀꢀꢀꢀꢀresultꢀ=ꢀSeparatedAmount();ꢀ ꢀꢀif(_isRetired)ꢀꢀ
ꢀꢀꢀꢀelseꢀ{ꢀ ꢀꢀꢀꢀreturnꢀRetiredAmount();ꢀ
ꢀꢀꢀꢀꢀꢀif(_isRetired)ꢀꢀ ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀresultꢀ=ꢀRetiredAmount();ꢀ ꢀꢀreturnꢀNormalAmount();ꢀ
ꢀꢀꢀꢀꢀꢀelseꢀꢀ
}ꢀ
ꢀꢀꢀꢀꢀꢀꢀꢀresultꢀ=ꢀNormalAmount();ꢀ
ꢀꢀꢀꢀ}ꢀ
ꢀꢀ}ꢀ
ꢀꢀreturnꢀresult;ꢀ
}ꢀ
ꢀ
Replace Conditional with Polymorphism
When
•ꢀ There are conditions depending on object type
Why
•ꢀ Increase extensibility
•ꢀ Improve code readability
Replace Conditional with Polymorphism
classꢀEmployeeꢀ{ꢀ
ꢀꢀpublicꢀdoubleꢀPayAmount()ꢀ{ꢀ
ꢀꢀꢀꢀswitchꢀ(_type)ꢀ{ꢀ
ꢀꢀꢀꢀꢀꢀcaseꢀEmployeeType.Engineer:ꢀreturnꢀ_salary;ꢀ
ꢀꢀꢀꢀꢀꢀcaseꢀEmployeeType.Salesman:ꢀreturnꢀ_salaryꢀ+ꢀ_commission;ꢀ
ꢀꢀꢀꢀꢀꢀcaseꢀEmployeeType.Manager:ꢀreturnꢀ_salaryꢀ+ꢀ_bonus;ꢀ
ꢀꢀꢀꢀꢀꢀdefault:ꢀthrowꢀnewꢀWrongEmployeeTypeException();ꢀꢀ
ꢀꢀꢀꢀ}ꢀ
ꢀꢀ}ꢀ
Employee
+PayAmount()
When
•ꢀ Method name doesn't show its intention
Why
•ꢀ Improve code readability
Rename Method
SecurityPrice
+LowerLimitExceed()
SecurityPrice
+IsLowerLimitExceeded()
Separate Query from Modifier
Create methods for query and for modification
When
•ꢀ Method returning value modifies object state
Why
•ꢀ Simplify interface
Separate Query from Modifier
publicꢀList<Employee>ꢀFindRetired(List<Employee>ꢀemployees)ꢀ{ꢀ
ꢀꢀvarꢀresultꢀ=ꢀnewꢀList<Employee>();ꢀ
ꢀꢀforeach(varꢀempꢀinꢀemployees)ꢀ{ꢀ
ꢀꢀꢀꢀif(emp.IsRetired)ꢀ{ꢀ
ꢀꢀꢀꢀꢀꢀAddBonus(emp);ꢀ
ꢀꢀꢀꢀꢀꢀresult.Add(emp);ꢀ
ꢀꢀꢀꢀ}ꢀ
ꢀꢀ}ꢀ
ꢀꢀreturnꢀresult;ꢀ
}ꢀ
publicꢀList<Employee>ꢀFindRetired(List<Employee>ꢀemployees)ꢀ{ꢀ
ꢀꢀreturnꢀemployees.Where(empꢀ=>ꢀemp.IsRetired).ToList();ꢀꢀꢀ
}ꢀ
publicꢀvoidꢀAddBonusToRetired(List<Employee>ꢀemployees)ꢀ{ꢀ
ꢀꢀforeach(varꢀempꢀinꢀemployees)ꢀ{ꢀ
ꢀꢀꢀꢀif(emp.IsRetired)ꢀ
ꢀꢀꢀꢀꢀꢀAddBonus(emp);ꢀ
ꢀꢀ}ꢀ
}ꢀ
ꢀ
Preserve Whole Object
Send the whole object to the method
When
•ꢀ Method has several object field values as params
Why
•ꢀ Simplify interface
Preserve Whole Object
DateTimeꢀstartꢀ=ꢀPeriod.Start;ꢀ
DateTimeꢀendꢀ=ꢀPeriod.End;ꢀ
List<Event>ꢀeventsꢀ=ꢀschedule.FindEvents(start,ꢀend);ꢀ
ꢀ
ꢀ
ꢀ
List<Event>ꢀeventsꢀ=ꢀschedule.FindEvents(Period);ꢀ
Introduce Parameter Object
Replace method parameters with an object
When
•ꢀ Method accepts several related parameters
Why
•ꢀ Simplify interface
Introduce Parameter Object
Schedule
+FindEvents(DateTime start, DateTime end)
Schedule
+FindEvents(Period period)
Replace Error Code with Exception
Throw exception instead of returning error code
When
•ꢀ Method returns error code
Why
•ꢀ Simplify interface
Replace Error Code with Exception
intꢀWithdraw(intꢀamount)ꢀ{ꢀ voidꢀWithdraw(intꢀamount)ꢀ{ꢀ
ꢀꢀifꢀ(amountꢀ>ꢀ_balance)ꢀ{ꢀ ꢀꢀifꢀ(amountꢀ>ꢀ_balance)ꢀꢀ
ꢀꢀꢀꢀreturnꢀ-1;ꢀ ꢀꢀꢀꢀthrowꢀnewꢀBalanceException();ꢀ
ꢀꢀ}ꢀ ꢀ
ꢀꢀelseꢀ{ꢀ ꢀꢀ_balanceꢀ-=ꢀamount;ꢀ
ꢀꢀꢀꢀ_balanceꢀ-=ꢀamount;ꢀ }ꢀ
ꢀꢀꢀꢀreturnꢀ0;ꢀ
ꢀꢀ}ꢀ
}ꢀ
Replace Exception with Test
Put conditional instead of throwing exception
When
•ꢀ Exception is used non-exceptional case
Why
•ꢀ Improve code readability
Replace Exception with Test
publicꢀdoubleꢀValueForPeriod(intꢀperiodIndex)ꢀ{ꢀ
ꢀꢀtryꢀ{ꢀ
ꢀꢀꢀꢀreturnꢀ_values[periodIndex];ꢀ
ꢀꢀ}ꢀ
ꢀꢀcatchꢀ(IndexOutOfRangeExceptionꢀe)ꢀ{ꢀ
ꢀꢀꢀꢀreturnꢀ0;ꢀ
ꢀꢀ}ꢀ
}ꢀ
publicꢀdoubleꢀValueForPeriod(intꢀperiodIndex)ꢀ{ꢀ
ꢀꢀifꢀ(periodIndexꢀ>=ꢀ_values.length)ꢀꢀ
ꢀreturnꢀ0;ꢀ
ꢀꢀreturnꢀ_values[periodIndex];ꢀ
}ꢀ
Generalization Refactorings
Pull Up and Push Down Method/Field.
Replace Inheritance with Delegation.
Extract Subclass/Superclass.
Pull Up Field/Method
Move method/field into the superclass
When
•ꢀ The same method/field is in several child classes
Why
•ꢀ Remove duplication
Pull Up Field/Method
Employee
Salesman Engineer
+name +name
Employee
+name
Salesman Engineer
Push Down Field/Method
Move method/field into the subclass
When
•ꢀ Superclass has method/field used only in one child
Why
•ꢀ Simplify design
Push Down Field/Method
Employee
+ComissionRate
Salesman Engineer
Employee
Salesman Engineer
+ComissionRate
Extract Subclass
Create subclass for subset of features
When
•ꢀ Subset of features used only in some instances
Why
•ꢀ Improve code readability
Extract Subclass
Event
+Start: DateTime
+Duration: TimeSpan
Event
+Start: DateTime
+Duration: TimeSpan
+Repeat: EventPeriodicity
PeriodicEvent
+Repeat: EventPeriodicity
Extract Superclass
Create superclass and move common features
When
•ꢀ Several classes have the same subset of features
Why
•ꢀ Remove duplication
Extract Superclass
AccountTransaction Transaction
Order
+Amount: int Order AccountTransaction
+Price: decimal
+Change: decimal +Amount: int +Saldo: decimal
+Security: Security +Price: decimal
+TradeDate: DateTime +Security: Security
Replace Inheritance with Delegation
Put superclass to a field and use delegation
When
•ꢀ Unable to say “subclass is a superclass”
•ꢀ Subclass implements superclass interface partially
Why
•ꢀ Simplify design
Replace Inheritance with Delegation
Array Array
+Insert(T item) +Insert(T item)
+IsEmpty() +IsEmpty()
-array
Stack Stack
+Push(T Item) +Push(T Item)
+Pop(): T +Pop(): T
+IsEmpty()
return _array.IsEmpty();
Architectural Refactorings
Tease Apart Hierarchies. Extract Hierarchy.
Convert Procedural Design to Objects.
Separate Domain from Presentation.
Tease Apart Inheritance
Create two hierarchies using one another
When
•ꢀ The inheritance hierarchy has two responsibilities
Why
•ꢀ Simplify design
Tease Apart Inheritance
*
Security
Security
Action Bond
Action Bond 1
SecurityOperation
SoldAction DeliveredBond
Delivery Selling
Extract Hierarchy
Create hierarchy with subclass per special case
When
•ꢀ The single class overwhelmed with conditionals
Why
•ꢀ Improve code readability
•ꢀ Improve architecture
Extract Hierarchy
Logger
+SendEmail(message)
+WriteToFile(message)
+WriteToSystemLog(message)
Logger
When
•ꢀ The classes with long methods but without fields
•ꢀ The classes with fields but without methods
Why
•ꢀ Improve code readability
•ꢀ Improve architecture
Convert Procedural Design to Objects
OrderCalculator
OrderLine Order
+CalculatePrice(order)
+CalculateTaxes(order)
Order OrderLine
+Price() 1 * +Price()
+Taxes() +Taxes()
Separate Domain from Presentation
Create domain logic classes separated from ui
When
•ꢀ UI logic is mixed with domain logic
Why
•ꢀ Improve code readability
•ꢀ Improve architecture
Separate Domain from Presentation
ClientPage
(business logic embedded)
1
ClientPage Client
Thank you!