300 likes | 387 Views
A Formal Foundation for Software Refactoring. Tom Mens, Serge Demeyer, Dirk Janssens tom.mens@vub.ac.be { serge.demeyer | dirk.janssens }@ua.ac.be Programming Technology Lab Lab on Re-Engineering Vrije Universiteit Brussel Universiteit Antwerpen. Goal.
E N D
A Formal Foundation forSoftware Refactoring Tom Mens, Serge Demeyer, Dirk Janssens tom.mens@vub.ac.be { serge.demeyer | dirk.janssens }@ua.ac.be Programming Technology Lab Lab on Re-Engineering Vrije Universiteit Brussel Universiteit Antwerpen
Goal • Improve tool support for refactoring object-oriented software … • less ad hoc • more scalable (e.g., composite refactorings) • more language independent • more correct (e.g., guarantee behaviour preservation) • … by providing a formal model in terms of graphs and graph rewriting
Why refactoring? • Refactorings are software transformations that restructure an object-oriented application while preserving its behaviour. • According to Fowler (1999), refactoring • improves the design of software • makes software easier to understand • helps you find bugs • helps you program faster
Why graph rewriting? • Graphs • provide a compact and expressive representation of program structure and behaviour • have a 2-D nature that allows to remove redundancy in the source code (e.g., more localised naming) • Graph rewriting • provide an intuitive description for the manipulation/transformation of complex graph-like structures • offer theoretical results which help in the analysis of such structures • confluence property, parallel/sequential independence, (de)composition
Important research questions • Which program properties should be preserved by refactorings? • input/output behaviour • timing constraints • static (compile-time) versus dynamic (run-time) behaviour • … • What is the complexity of a refactoring? • complexity of applicability of a refactoring • complexity of applying the refactoring (e.g. high change impact) • How do refactorings affect quality factors? • increase/decrease program complexity, understandability, maintainability, …
Important research questions ctd. • How can refactorings be composed/decomposed? • combining primitive refactorings in more complex ones • extracting a sequence of refactorings from two successive versions of a program • How do refactorings interact? • parallel application of refactorings may lead to consistency problems • How should we provide support for non-behaviour-preserving refactorings? • How do refactorings affect design models? • co-evolution • Is it possible to define a language-independent formalism?
1. originate(p) 3. accept(p) 2. send(p) 4. send(p) 5. accept(p) 8.print(p) 6. send(p) 7. accept(p) Running example: LAN simulation workstation 1 workstation 3 fileserver 1 workstation 2 printer 1
UML class diagram originator Node Packet nextNode name contents addressee accept(p:Packet) send(p:Packet) Workstation PrintServer FileServer accept(p:Packet) save(p:Packet) accept(p:Packet) print(p:Packet) accept(p:Packet) originate(p:Packet)
public class Node { public String name; public Node nextNode; public void accept(Packet p) { this.send(p); } protected void send(Packet p) { System.out.println( name + "sends to" + nextNode.name); nextNode.accept(p); } } public class Packet { public String contents; public Node originator; public Node addressee; } public class Printserver extends Node { public void print(Packet p) { System.out.println(p.contents); } public void accept(Packet p) { if(p.addressee == this) this.print(p); else super.accept(p); } } public class Workstation extends Node { public void originate(Packet p) { p.originator = this; this.send(p); } public void accept(Packet p) { if(p.originator == this) System.err.println("no destination"); else super.accept(p); } } Java source code
Graph representation – part 1 • program structure
Type graph – part 1 • Node types • C class • B method body • A attribute (variable) • M method signature (selector) • P (formal) parameter • Edge types • T: A|P|M C attribute type, parameter type, method return type • L: M B dynamic method lookup • I: C Cinheritance • A|BC, PM containment
Graph representation – part 2 • program behaviour for class Node
Type graph – part 2 • Node types • E (sub)expression in the method body • Edge types • E: B E top-level expression in method body • E: E E cascaded message send • A: E P|Aaccess of parameter or attribute • U: E A attribute update • S: E Bstatic call (e.g., super call or call to static method) • D: E Mdynamic call (late binding, virtual function call) • P: E P|E|A actual parameter (of a call or attribute update) • this: E C explicit reference to this object
Graph representation – part 3 • program behaviour for class Workstation
Behaviour preservation invariants • Access preservation • each method body (indirectly) performs at least the same attribute accesses as it did before the refactoring • Update preservation • each method body (indirectly) performs at least the same attribute updates as it did before the refactoring • Statement preservation • each method (indirectly) performs at least the same statements as it did before the refactoring • Type preservation • each statement in each method body still has the same result type or return type as it did before the refactoring
Refactoring – Encapsulate Field Fowler 1999, page 206 There is a public field Make it private and provide accessors public class Node { private String name; private Node nextNode; public String getName() { return this.name; } public void setName(String s) { this.name = s; } public Node getNextNode() { return this.nextNode; } public void setNextNode(Node n) { this.nextNode = n; } public void accept(Packet p) { this.send(p); } protected void send(Packet p) { System.out.println( this.getName() + "sends to" + this.getNextNode().getName()); this.getNextNode().accept(p); } } public class Node { public String name; public Node nextNode; public void accept(Packet p) { this.send(p); } protected void send(Packet p) { System.out.println( name + "sends to" + nextNode.name); nextNode.accept(p); } }
Refactoring – Encapsulate Field • before the refactoring
Refactoring – Encapsulate Field • after the refactoring
Graph transformation – Encapsulate Field • EncapsulateField(class,attr,type,accessor,updater)
Behaviour preservation – Encapsulate Field • EncapsulateField preserves behaviour • access preserving: all attribute nodes can still be accessed via a transitive closure • update preserving: all attribute nodes can still be updated via a transitive closure D L E A A attr A accessor M B attr A E E E 3 5 3 5 D L E U U attr A updater M B attr A E E E 3 4 3 4 Behaviour preservation invariants detected by graph patterns
Refactoring – Extract Method Fowler 1999, page 110 You have a code fragment that can be grouped together Turn the fragment into a method whose name explains the purpose of the method public class Node { ... public void accept(Packet p) { this.send(p); } protected void send(Packet p) { this.log(p); this.getNextNode().accept(p); } protected void log(Packet p) { System.out.println( this.getName() + "sends to" + this.getNextNode().getName()); } } public class Node { ... public void accept(Packet p) { this.send(p); } protected void send(Packet p) { System.out.println( this.getName() + "sends to" + this.getNextNode().getName()); this.getNextNode().accept(p); } }
Refactoring – Extract Method • before the refactoring
Refactoring – Extract Method • after the refactoring
Graph transformation – Extract Method • ExtractMethod(class,old,new,StatList) • given the body of method old in class, redirect all statements in StatList to the body of a parameterless method new 2 2 3 StatList 3 1 1 (method parameters can be introduced afterwards by AddParameter)
Behaviour preservation – Extract Method • ExtractMethod preserves behaviour • statement preserving: all expressions (calls, accesses, updates) that were performed before the refactoring, are still performed (via transitive closure) after the refactoring L E L E D L E old M B old M B new M B E E E a a 1 1 2 3 2 3 StatList Behaviour preservation invariant detected by graph pattern
Refactoring – Replace data value with object Fowler 1999, page 175 You have a data item that needs additional data or behaviour Turn the data item into an object public class Packet { ... private Document doc; public String getContents() { return this.doc.contents; } public void setContents(String s) { this.doc.contents = s; } } public class Document { ... public String contents; } public class Packet { ... private String contents; public String getContents() { return this.contents; } public void setContents(String s) { this.contents = s; } }
Refactoring – Replace data value with object • before the refactoring
Refactoring – Replace data value with object • after the refactoring
Graph transformation – Replace data value with object • ReplaceDataValueWithObject(class,attr,attrType,object,objType)
Behaviour preservation – Replace data value with object • ReplaceDataValueWithObject preserves behaviour • access preserving • update preserving • type preserving Behaviour preservation invariants detected by graph patterns