1 / 30

A Formal Foundation for Software Refactoring

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.

Download Presentation

A Formal Foundation for Software Refactoring

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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

  2. 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

  3. 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

  4. 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

  5. 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, …

  6. 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?

  7. 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

  8. 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)

  9. 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

  10. Graph representation – part 1 • program structure

  11. 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|BC, PM containment

  12. Graph representation – part 2 • program behaviour for class Node

  13. 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

  14. Graph representation – part 3 • program behaviour for class Workstation

  15. 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

  16. 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); } }

  17. Refactoring – Encapsulate Field • before the refactoring

  18. Refactoring – Encapsulate Field • after the refactoring

  19. Graph transformation – Encapsulate Field • EncapsulateField(class,attr,type,accessor,updater)

  20. 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

  21. 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); } }

  22. Refactoring – Extract Method • before the refactoring

  23. Refactoring – Extract Method • after the refactoring

  24. 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)

  25. 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

  26. 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; } }

  27. Refactoring – Replace data value with object • before the refactoring

  28. Refactoring – Replace data value with object • after the refactoring

  29. Graph transformation – Replace data value with object • ReplaceDataValueWithObject(class,attr,attrType,object,objType)

  30. Behaviour preservation – Replace data value with object • ReplaceDataValueWithObject preserves behaviour • access preserving • update preserving • type preserving Behaviour preservation invariants detected by graph patterns

More Related