Title: Refactoring with Contracts
1Refactoring with Contracts
2Is Software Development a Pain??
- Of course not! It's fun!
- But the requirements are always changing
- Dan Berry (2002)
- Every development method has a fatal flaw, a pain
that developers postpone and avoid - In XP, this is refactoring
- For us, it is unit testing!
3What Seems to be the Problem?
- Writing all the necessary tests is hard work
- Unit tests need to change with almost any change
in the code - Functionality
- Refactoring
- Test data generation needed
4Solution
- Tests should be responsible for exercising the
code - The correctness is to be checked by the contract!
5Agenda
- Introduction
- Refactoring Examples
- Crepe Recipe
- Summary
6Agenda
- Introduction
- Refactoring Examples
- Crepe Recipe
- Summary
7Design by Contract
- A practical methodology for evolving code
together with its specification - Class invariants, method preconditions and
postconditions - Precondition binds clients
- Postcondition obliges suppliers
- Invariant is a consistency constraint
8Behavioral Subtyping (Liskov)
- Invariants and postconditions in subclasses
- May only be strengthened
- Computed as conjunctions (ANDed)
- Preconditions in subclasses
- May only be weakened
- Computed as disjunctions (ORed)
9Extreme Design by Contract
- Correctness checked by contract
- Of course, contract may need to change when code
changes - But Contract expresses intent of code!
- Contracts can be refactored systematically
- Contract refactoring can be partially automated
Crepe Contract Refactoring Plugin for Eclipse
10Agenda
- Introduction
- Refactoring Examples
- Crepe Recipe
- Summary
11ReadyQueue (1)
- / _at_inv size() gt 0 /
- public class ReadyQueue
-
- final private List elements new LinkedList()
- public int size() / ... /
- / _at_pre job ! null
- _at_pre job.status() Job.READY
- _at_post elements.get(0) job
- _at_post size() prev(size()) 1
- /
- public void insert(Job job) / ... /
12ReadyQueue (2)
- / _at_pre size() gt 0
- _at_post size() prev(size()) - 1
- /
- public void remove() / ... /
- / _at_post (size() 0) (ret null)
- /
- public Job top() / ... /
13FreeJobs (1)
- / _at_inv size() gt 0 /
- public class FreeJobs
-
- final private List elements new LinkedList()
- public int size() / ... /
- / _at_pre job ! null
- _at_pre job.status() Job.INACTIVE
- _at_post top() job
- _at_post size() prev(size()) 1
- /
- public void insert(Job job) / ... /
READY
elements.get(0) job
14FreeJobs (2)
- / _at_pre size() gt 0
- _at_post size() prev(size()) - 1
- /
- public void remove() / ... /
- / _at_pre size() gt 0
- _at_post ret ! null
- _at_post ret.status() Job.INACTIVE
- /
- public Job top() / ... /
15Example Extract Superclass
16Extract Superclass Refactoring (1)
- / _at_inv size() gt 0 /
- public abstract class JobDispenser
- protected List elements new LinkedList()
- / _at_pre false
- _at_post size() 1 prev(size())
- /
- public abstract void insert(Job job)
Contradictory preconditions in subclasses _at_pre
job.status() Job.READY _at_pre job.status()
Job.INACTIVE
17Extract Superclass Refactoring (2)
- / _at_pre size() gt 0
- _at_post 1 size() prev(size())
- /
- public abstract void remove()
- / _at_pre size() gt 0
- /
- public abstract Job top()
- public abstract int size()
Common postconditions (Form slightly changed by
theorem-prover)
Precondition from FreeJobs, but not from
ReadyQueue
18Changes ReadyQueue (1)
- / _at_inv size() gt 0 /
- public class ReadyQueue
-
- / _at_pre job ! null
- _at_pre job.status() Job.READY
- _at_post elements.get(0) job
- _at_post size() prev(size()) 1
- /
- public void insert(Job job) / ... /
19Changes ReadyQueue (2)
- / _at_pre size() gt 0
- _at_post size() prev(size()) - 1
- /
- public void remove() / ... /
- public int size() / ... /
- / _at_pre size() 0
- _at_post (size() 0) (ret null)
- /
- public Job top() / ... /
20Changes FreeJobs (1)
- / _at_inv size() gt 0 /
- public class FreeJobs
-
- / _at_pre job ! null
- _at_pre job.status() Job.INACTIVE
- _at_post top() job
- _at_post size() prev(size()) 1
- /
- public void insert(Job job) / ... /
21Changes FreeJobs (2)
- / _at_pre size() gt 0
- _at_post size() prev(size()) - 1
- /
- public void remove() / ... /
- / _at_pre size() gt 0
- _at_post ret ! null
- _at_post ret.status() Job.INACTIVE
- /
- public Job top() / ... /
- public int size() / ... /
22PriorityQueue
- / _at_inv size() gt 0 /
- public class PriorityQueue
- / _at_pre job ! null
- _at_pre job.status() Job.READY
- _at_pre job.priority()lt Scheduler.current_priori
ty() - _at_post size() prev(size()) 1
- /
- public void insert(Job job) / ... /
- // ...
23Example Add Inheritance
ReadyQueue
ReadyQueue
PriorityQueue
PriorityQueue
24PriorityQueue vs. ReadyQueue
- / _at_inv size() gt 0 /
- public class ReadyQueue
- / _at_pre job ! null
- _at_pre job.status() Job.READY
- _at_post elements.get(0).equals(job)
- _at_post size() prev(size()) 1 /
- public void insert(Job job) / ... /
-
- / _at_inv size() gt 0 /
- public class PriorityQueue
- / _at_pre job ! null
- _at_pre job.status() Job.READY
- _at_pre job.priority()lt Scheduler.current_priori
ty() - _at_post size() prev(size()) 1 /
- public void insert(Job job) / ... /
Extra postcondition
Extra precondition
25Add Inheritance
- Cannot add inheritance relationship between two
classes if, in the subclass, it causes - Preconditions to be strengthened
- Invariants or postconditions to be weakened
26Add Inheritance (Crepe)
27Agenda
- Introduction
- Refactoring Examples
- Crepe Recipe
- Summary
28The Crepe Recipe
- Use Eclipse ASTParser on assertions
- Parse the assertions in the Javadoc
- Convert to predicate form
- Use theorem prover (Mathematica) to verify
relationships between assertions - Find contract contradictions
- Compute the new contract
- Combine existing assertions to create new ones
- Compute contracts for arbitrary code (in
preparation)
29Combining Assertions
- Preconditions are conjoined and simplified
- Postconditions can be disjoined
- But, sometimes the most precise assertions are
not best!
30Extract Superclass - Postconditions
- class A
- / _at_post x gt 0
- _at_post x lt 10
- _at_post y gt 0
- _at_post y lt 10
- /
- void foo()
- class B
- / _at_post x gt 10
- _at_post x lt 20
- _at_post y gt 10
- _at_post y lt 20
- /
- void foo()
31Postconditions Computation
class AB_alternative / _at_post x gt 0
_at_post x lt 20 _at_post y gt 0 _at_post y lt 20
/ void foo()
class AB_strongest / _at_post (x gt 0 x
lt 10 y gt 0 y lt 10) (x gt 10
x lt 20 y gt 10 y lt 20) / void
foo()
32Postconditions Computation
class A extends AB_alternative / _at_post x lt
10 _at_post y lt 10 / void foo()
class B extends AB_alternative / _at_post x gt
10 _at_post y gt 10 / void foo()
20
10
A
10
20
33Agenda
- Introduction
- Refactoring Examples
- Crepe Recipe
- Summary
34Contracts Refactoring Statistics
35Crepe Limitations
- Contract computation from arbitrary code
- Theorem prover (Mathematica)
- Supports only Booleans, Integers, Doubles, and
Reals - Fails to simplify some expressions using
assumptions - Not open source
- Assertions parsing
- Return type computations
36Summary
- Design by Contract is an essential technique for
producing high-quality code - It is synergistic with Agile practices
- It helps develop with confidence
- Automation needed for the process of refactoring
with contracts - Crepe demonstrates such automating techniques
37Questions?
38