10 RefactoringToPatterns
10 RefactoringToPatterns
Refactoring To Patterns
Introduction
Design Patterns
Refactoring to Patterns
Conclusions
2
The Reengineering Life-Cycle
(3) problem
(4) problem
detection
resolution
Designs
(2) model
capture
Code
3
Object-Oriented Design Patterns
4
Design Patterns Idea
5
Elements of Design Patterns
Pattern name
Design Vocabulary
Problem
When to apply the pattern?
List of preconditions
Solution
Abstract description of a design problem and how a general arrangements of
classes and objects solves it
Consequences
Impact on flexibility, extensibility, portability, etc.
6
Describing Design Patterns
7
Design Patterns (GoF) Classification
Purpose
Creational Structural Behavioral
Scope Class Factory Method Adapter (class) Interpreter
Template Method
8
Additional Reading
Online: http://sourcemaking.com/design_patterns
9
Refactoring to Patterns
Design Patterns and Refactorings
[Fowler 1999]
11
Refactoring to Patterns: Book
Creation
Move functionality to create instances of complex classes to Factory and
Builder classes
Simplification
Simplify the source code by introducing strategy, state, commands, composites,
and decorators
Generalization
Transform specific code into general-purpose code by introducing Template
Method, Composite, Observer, Adapter, and Interpreter
13
Overview of Refactorings (cont.)
Protection
Protect existing code from modifications by introducing a Singleton and Null
Object
Accumulation
Accumulating information by introducing a Visitor
14
Refactoring to Patterns: Examples
Factory Method
Introduce Polymorphic Creation with Factory Method
State
Factor out State
Observer
Replace Hard-Coded Notifications with Observer
15
Example 1: Creation
17
Introduce Factory Method
Problem
Classes in a hierarchy implement a method similarly, except for an object
creation step
Solution
Make a single superclass version of the method that calls a Factory Method to
handle the instantiation
18
Factory Method Pattern
Intent
Define an interface for creating an object, but let subclasses decide which class
to instantiate.
Motivation
Encapsulate the knowledge of which subclass to create to a factory method
Applicability
Class wants its subclasses to specify the objects it creates
19
Factory Method Pattern: Structure
Product Creator
FactoryMethod() product = FactoryMehod()
AnOperation()
ConcreteProduct ConcreteCreator
return new ConcreteProduct()
FactoryMethod()
20
Example BuilderTest: Class Diagram
TestCase
DomBuilderTest XMLBuilderTest
testAddAboveRoot(): void testAddAboveRoot(): void
... ...
builder = new XMLBuilder("orders"); builder = new XMLBuilder("orders");
... ...
OutputBuilder
DOMBuilder XMLBuilder
21
Introduce Factory Method: Mechanics
22
Extract Method on creation code
public class DOMBuilderTest extends TestCase {
private OutputBuilder builder;
private OutputBuilder createBuilder(String rootName) {
return new DOMBuilder(“orders”);
}
public void testAddAboveRoot() {
String invalidResult = “<orders> ... </orders>”;
builder = createBuilder(“orders”);
builder.addBelow(“order”);
...
}
}
public class XMLBuilderTest extends TestCase {
private OutputBuilder builder;
private OutputBuilder createBuilder(String rootName) {
return new XMLBuilder(“orders”);
}
public void testAddAboveRoot()
...
builder = createBuilder(“orders”);
...
}
} 23
Extract Superclass
public abstract class AbstractBuilderTest extends TestCase {
24
Form Template Method and Pull Up Field
public abstract class AbstractBuilderTest extends TestCase {
protected OutputBuilder builder;
25
Result Refactoring BuilderTest
TestCase
FactoryMethod
AbstractBuilderTest
...
builder : OutputBuilder
builder = createBuilder("orders");
createBuilder(rootName:String) : OutputBuilder
...
testAddAboveRoot() : void
ConcreteCreator
DomBuilderTest XMLBuilderTest
createBuilder(rootName:String): OutputBuilder createBuilder(rootName:String): OutputBuilder
26
Benefits and Liabilities
27
Example 2: Simplicfication
29
Example SystemPermission (cont.)
...
public void claimedBy(SystemAdmin admin) {
if (!state.equals(REQUESTED)) return;
willBeHandledBy(admin);
state = CLAIMED;
}
Problem
How to make a class extensible whose behavior depends on a complex
evaluation of its state?
Solution
Eliminate complex conditional code over an object’s state by applying the
State Pattern
31
State Pattern
Intent
Allow an object to alter its behavior when its internal state changes. The
object will appear to change its class.
Applicability
An object’s behavior depends on its state, and it must change its behavior at
run-time depending on that state
Operations have large, multipart conditional statements that depend on the
object’s state.
32
State Pattern: Structure
Context State
Request() Handle()
ConcreteStateA ConcreteStateB
state.Handle() Handle() Handle()
33
Example SystemPermission: Class Diagram
SystemPermission
state : String
REQUESTES : String
CLAIMED : String
GRANTED : String if (! state.equals(REQUESTED) return;
DENIED : String willBeHandledBy(admin);
... state = CLAIMED;
claimedBy(...) : void
grantedBy(...) : void
deniedBy(...) : vodi
...
34
Change: Adding two more states
35
Replace Conditionals with State: Mechanics
Extract Subclass
To produce one subclass per constant (state) and declare state superclass as
abstract
Move Method
On context class methods that change the value of original state variable
36
Replace Type Code with State Class
public class PermissionState {
private String name;
37
Replace Type Code with State Class (cont.)
public class SystemPermission {
private PermissionState permissionState;
38
Extract Subclass for each State (Constant)
public abstract class PermissionState {
private String name;
private PermissionState(String name) {
this.name = name;
}
public final static PermissionState REQUESTED = new PermissionRequested();
...
}
39
Move State Trans. Logic to State Class
public abstract class PermissionState...
public void claimedBy(SystemAdmin admin, SystemPermission permission) {
if (!permission.getState().equals(REQUESTED) &&
!permission.getState().equals(UNIX_REQUESTED))
return;
permission.willBeHandledBy(admin);
if (permission.getState().equals(REQUESTED))
permission.setState(CLAIMED);
else if (permission.getState().equals(UNIX_REQUESTED)) {
permission.setState(UNIX_CLAIMED);
}
}
40
Move State Trans. Logic (cont.)
public class SystemPemission {
...
void setState(PermissionState state) { // now has package-level visibility
permissionState = state;
}
41
Move State Trans. Logic to Subclasses
public class PermissionRequested extends PermissionState {
...
public void claimedBy(SystemAdmin admin, SystemPermission permission) {
permission.willBeHandledBy(admin);
permission.setState(CLAIMED);
}
}
42
Move State-Transition to Subclasses (cont.)
public class PermissionClaimed extends PermissionState...
public void deniedBy(SystemAdmin admin, SystemPermission permission) {
if (!permission.getAdmin().equals(admin))
return;
permission.setIsGranted(false);
permission.setIsUnixPermissionGranted(false);
permission.setState(DENIED);
permission.notifyUserOfPermissionRequestResult();
}
44
Result Refactoring SystemPermission
permissionState.claimedBy(...);
PermissionState
name : String
REQUESTES : PermissionState
SystemPermission CLAIMED : PermissionState
GRANTED : PermissionState
claimedBy(...) : void permissionState DENIED : PermissionState
grantedBy(...) : void ...
deniedBy(...) : void PermissionState(name : String)
claimedBy(...) : void
grantedBy(...) : void
deniedBy(...) : vodi
PermissionClaimed PermissionRequested
grantedBy(...) : void claimedBy(...) : void
deniedBy(...) : void
Permission...
...
permission.willBeHandledBy(admin);
permission.setState(CLAIMED);
45
Benefits and Liabilities
46
Example 3: Generalization
Problem
Subclasses are hard-coded to notify a single instance of another class
Solution
Remove the subclass by making their superclass capable of notifying one or
more instances of any class that implements an observer interface
49
Observer Pattern
Intend
Maintain a dependency between a central object (Subject) and multiple
dependent objects (Observers)
Motivation
Decouple a subject from its observers
Applicability
When an instance must notify more than one receiver instance,
E.g., when there are various views (Observers) on the same model instance
(Subject)
50
Observer Pattern: Structure
Subject
attach(observer:Observer) : void Observer
observers
detach(observer:Observer) : void update() : void
notify() : void
ConcreteSubject ConcreteObserver
state : State subject observerState : State
getState() : State update() : void
observerState = subject.getState();
51
Example TestResult: Class Diagram
TestResults
textui.TextTestResults ui.UITestResults
Hard-Coded Notifications
textui.TestRunner ui.TestRunner
52
Repl. Notific. with Observer: Mechanics
53
Move Custom Behavior to Receiver
package textui;
public class TextTestResult extends TestResult {
private TestRunner runner;
TextTestResult(TestRunner runner) {
this.runner = runner;
}
public synchronized void addError(Test test, Throwable t) {
super.addError(test, t);
runner.addError(this, test, t);
}
}
package textui;
public class TestRunner ...
protected TextTestResult createdTestResult() {
return new TextTestResult(this);
}
54
Extract Observer Interface
public class TextTestResult extends TestResult ...
public synchronized void addError(Test test, Throwable t) {
super.addError(test, t);
runner.addError(this, test, t);
}
public synchronized void addFailure(Test test, Throwable t) {
super.addFailure(test, t);
runner.addFailure(this, test, t);
}
public synchronized void startTest(Test test) {
super.startTest(test);
runner.startTest(this, test);
}
}
public interface TestListener {
public void addError(TestResult testResult, Test test, Throwable t);
public void addFailure(TestResult testResult, Test test, Throwable t);
public void startTest(TetResult testResult, Test test);
public void endTest(TestResult testResult, Test test);
}
public class TestRunner implements TestListener ...
public void endTest(TestResult testResult, Test test) { }
} 55
Make Receiver Implement the Interface
package ui;
public class TestRunner extends Frame implements TestListener {
...
}
package ui;
public class UITestResult extends TestResult ...
protected TestListener runner;
UITestResult(TestListener runner) {
this.runner = runner;
}
}
package textui;
public class TextTestResult extends TestResult ...
protected TestListener runner;
TextTestResult(TestListener runner) {
this.runner = runner;
}
}
56
Pull Up Methods in Observers
public class TestResult ...
protected TestListener runner;
public TestResult() {
failures = new ArrayList<TestFailure>();
errors = new ArrayList<TestError>();
runTests = 0;
stop = false;
}
public TestResult(TestListener runner) {
this();
this.runner = runner;
}
public synchronized void addError(Test test, Throwable t) {
errors.add(new TestError(test, t));
runner.addError(this, test, t);
}
...
}
package ui;
public class UITestResult extends TestResult { }
package textui;
public class TextTestResult extends TestResult { } 57
Update Observers to Work with Subject
package textui;
public class TestRunner implements TestListener ...
protected TestResult createTestResult() {
return new TestResult(this);
}
}
package ui;
public class TestRunner implements TestListener ...
protected TestResult createTestResult() {
return new TestResult(this);
}
public synchronized void runSuite() {
...
TestResult result = createTestResult();
testSuite.run(testResult);
}
58
Update Subject to Notify many Observers
public class TestResult ...
protected List<TestListener> observers = new ArrayList<TestListener>();
59
Update Observer to Register with Subject
package textui;
public class TestRunner implements TestListener ...
protected TestResult createTestResult() {
TestResult testResult = new TestResult(this);
testResult.addObserver(this);
return testResult;
}
}
60
Result: Refactoring TestResult
TestResult TestListener
textui.TestRunner ui.TestRunner
61
Benefits and Liabilities
- Complicate design
When a hard-coded notification will suffice
When you have cascading notifications
62
More Refactorings To Patterns
Refactoring to Patterns
Known and tested solutions for similar design problems
Encapsulates and simplifies logic
Increases extensibility (interfaces, loose coupling)
64