Refactoring MSO 08/09, WP. What is refactoring ? Refactoring is the act of transforming the...
-
Upload
angel-davidson -
Category
Documents
-
view
235 -
download
0
Transcript of Refactoring MSO 08/09, WP. What is refactoring ? Refactoring is the act of transforming the...
Refactoring
MSO 08/09, WP
What is refactoring ?
Refactoring is the act of transforming the internal structure of a program while preserving its observable behavior.
The main motivation is to improve the 'quality' of your program: improve its readability make it less error prone improve modularity and extensibility
2
Systematic refactoring
3
Idea: rely on a set of small-step changes that are easy to understand and evidently correct.
Do refactoring as a series of such steps.
Fowler’s book (1999) contains a catalogue of refactoring steps.
See also www.refactoring.com
Objective & plan To make you aware of these concepts from software development:
Refactoring, bad smell, antipattern
We’ll discuss some examples.
Consult your Web-resource for the full catallogues: www.refactoring.com http://en.wikipedia.org/wiki/Anti-pattern
Buy the books “Refactoring” and “Antipatterns” as reference for you when you latter develop your software.
4
Systematic refactoring
Automated support by IDEs, e.g. in Eclipse.
for a small subset of Fowler's refactoring.
No formal proof of the correctness (on-going research).
Current practice: Relies on common sense. Prepare a good set of tests; between the steps, do testing
to check that you indeed don't change the program functionalities, nor introduce new bugs.
5
"introduce assertion"
6
class ApplicationLogic{ ... addSubscription(Customer c, Service s) {
// c and s should not be null
Subscription scr = new Subscription(c,s) ;
c.subscriptions.add(scr) ;
subscriptions.add(scr) ;
}
assert c!=null && s!=nullassert c!=null && s!=null less error prone
"extract method"
7
class Customer { ... print() { println("name " + name) ;
// calculate total weekly cost double c = 0 ; for (Subscription s : subscriptions) c += s.service.price() ; c = c/52.0 ;
println("Weekly cost = " + (int) c ) }
double total_weeklycost() { double c = 0 ; for (Subscription s : subscriptions) c += s.service.price() ; return c/52.0}
double total_weeklycost() { double c = 0 ; for (Subscription s : subscriptions) c += s.service.price() ; return c/52.0}
"extract method"
8
class Customer { ... print() { println("name " + name) double c = total_weeklycost() println("Weekly cost = " + (int) c ) }
double total_weeklycost() { double c = 0 ; for (Subscription s : subscriptions) c += s.service.price() ; return c/52.0}
double total_weeklycost() { double c = 0 ; for (Subscription s : subscriptions) c += s.service.price() ; return c/52.0}
improve readability
"extract method"
Consider again the original Customer class. We want a new feature: VIP customers.
They get discounts influence their price you have to adapt "print" too.
9
Customer
print()
Customer
print()
VIPVIP
Solution 1
You have to rewrite print in VIP, probably with lots of code duplication.
Customer
print()total_weeklycost()
Customer
print()total_weeklycost()
VIPVIP
Solution 2
extract method
improve modularity & extensibility
"move method"
10
class Service {
boolean isBigSpender(Customer c) {
if (c.total_weeklycost() < 1000) return false for (Subscription scr : c.subscriptions) if (scr.service == this) return true
return false }
class Customer {
boolean isBigSpender(Service s) {
if (total_weeklycost() < 1000) return false for (Subscription scr : subscriptions) if (scr.service == s) return true
return false }
class Customer {
boolean isBigSpender(Service s) {
if (total_weeklycost() < 1000) return false for (Subscription scr : subscriptions) if (scr.service == s) return true
return false }
class Service {
boolean isBigSpender(Customer c) { return c.isBigSpender(this) }
class Service {
boolean isBigSpender(Customer c) { return c.isBigSpender(this) }
reduce coupling less error prone
"Hide delegate"
11
class Customer {
double total_weeklycost() { double c = 0 ; for (Subscription s : subscriptions) c += s.service.price() ; return c/52.0 }
CustomerCustomer
SubscriptionSubscription
Serviceprice()Serviceprice()
0..*
CustomerCustomer
Subscriptionprice()Subscriptionprice()
Serviceprice()Serviceprice()
0..*
hide delegate
s.price()s.price()
reduce coupling
However ...
Refactoring tends to add indirection (small) run-time overhead a "logic" that was centralized can become scattered
Refactoring may change a class "interface" E.g. "move field", "remove parameter" Problem if you have a client that is beyond your reach. Don't publish your packages too early?
12
Bad smells
‘If it stinks, change it’ (user manual for babies)
A 'bad smell' describes a set of characteristics indicating that refactoring may be wise.
Fowler has a list of smells in his book.
See also the list in MSO website.
13
Bad smells
(Substantial) duplication of code Overly long method Overly large class Too many attributes in a class Method with lots of parameters
... etc. See the list in MSO website.
We'll discuss some selected examples.
14
Bad smell : shotgun surgery
You need to change a feature but are forced to do this in many places.
15
Customer
print()
Customer
print()
Service
print()
Service
print()
E.g. you want to change how the Company logo is printed, but is forced to change the "print" method of both Customer and Service to do this.
Let's try refactoring :
1."extract method"2."extract superclass"3."replace inheritance with delegation"
CompanyLogo
printlogo()
CompanyLogo
printlogo()
Bad smell : primitive obsession You use a primitive type for representing a 'concept'.
Example:
16
Customer
name : Stringemail : String
Customer
name : Stringemail : String
Then at some point you need a method to validate an email. Where to put this method?
Customer
name : String
Customer
name : String
validate()
validate()
We'll do "replace data with object" refactoring:
Bad smell: switch statement This is not to say that "switch" is bad; but consider this
example:
17
switch (customer.code) { VIP : registrationfee = 2000 ; break ; PREMIUM : registrationfee = 1500 ; break ; default : registrationfee = 1000 ; }
...
switch (customer.code) { VIP : discount = .3 ; break ; PREMIUM : discount = 0.1 ; break ; default : discount = 0 ;}
Anti patterns
18
Help! Another caltalogue of “nasties” ??
Anti pattern: pattern that initially appears beneficial, but
ultimately produces more problems than benefits.
it has a (refactoring) solution.
Brown et al: “AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis”, 1998.
Antipattern at different levels “paralysis by analysis” (management level)
Let’s allocate lots of effrot to do good design!
But you over do it blocking the implementation phase.
Or ending up writing design documents with unproportional add-value.
Solution: turn the process to be more agile.
19
A single big cycle of design-implement-test
Several smaller cycles of design-implement-test
Antipattern at different levels
“Softwarebloat” (design level)
You want to reach out to a wide spectrum of users. To make them all happy you add LOTS of features.
Big software expensive to maintain. Use lots of resources, and slow.
Solution: make features to be plugable.
20
“Blob” antipattern (code level) Lots of functionalities are coded into one class, e.g. because:
that’s the simplest way of coding them at the moment the programmer lacks OO experience
Problem: long term modularity and extensibility
Solution: Fowler’s refactoring
21
Blob, example
22
Library
fineRate: floatborrowings: ...
doInventory()checkOut(Book, Client)checkIn(Book)add(Book)delete(Book)printCatalog()searchCatalog(String): Bookedit(Book)print(Book)add(Client)booksBorrowed(Client): Set...currentBorrower(Book): ClientprintCard(Client)calculateFine(Client): float
Bookauthor: Stringtitle: Stringid: String
Clientname: Stringaddressid: String
0..*
0..*
“Object Orgy” antipattern (code level) Free access to an object’s (of class
C) internal state, e.g. because:
Convenient for other classes that use C.
Problem:
As your software grows, the logic becomes increasingly defragmented hard to maintain.
Solution: refactor.
23
Library
+ fineRate: float+ borrowings: Map<Client,List<bool>>+ clients : Set<Client>+ books : Set<Book>
doInventory()checkOut(Book, Client)checkIn(Book)add(Book)delete(Book)printCatalog()searchCatalog(String): Bookedit(Book)print(Book)add(Client)booksBorrowed(Client): Set<Book>currentBorrower(Book): ClientprintCard(Client)calculateFine(Client): float
Final note on refactoring
Use smartly don’t do it obsessively blocking the actual work. Plan ahead pre-allocate resources for refactoring
When your software starts to smell really bad, you’re in urgent need to refactor. Hey, I have pre-allocated resources, Yay!
When not to refactor If the code is still unstable Shortly before a hard deadline If it is more efficient to just redo from scratch
24