OBJECT ORIENTED REFACTORINGS structural equalities in object oriented programs Ref2-1 Refactorings 1.model refactoring 2.object oriented refactorings

Embed Size (px)

DESCRIPTION

Fowler’s Text and Web Site Fowler has collected and described a large number of common OO refactorings Some covered in this lecture Others, not part of Fowler’s collection, are also covered See: Ref2-3 refactoring.com/catalog/index.html

Citation preview

OBJECT ORIENTED REFACTORINGS structural equalities in object oriented programs Ref2-1 Refactorings 1.model refactoring 2.object oriented refactorings Introduction A refactoring is a semantics-preserving transformation that maps an input program to an output program behavior of the program is the same at least, that is the goal structure should be cleaner or easier to understand or extend Now known that refactorings apply to all program representations like model refactorings of the last lecture Ref2-2 Fowlers Text and Web Site Fowler has collected and described a large number of common OO refactorings Some covered in this lecture Others, not part of Fowlers collection, are also covered See: Ref2-3 refactoring.com/catalog/index.html Big Picture A refactoring is an endogenous transformation domain, codomain are the same should be bi-directional it has an inverse some directions are harder to implement than others each direction is usually given a distinct name A refactoring should be a theorem on the semantic equivalence of the input and output diagrams not something to be liked or disliked interestingly, correctness proofs for refactorings are scant rely on programming language semantics, which makes proofs hard you must experiment to understand the semantics of a refactoring yuck consequently expect to find refactoring engines that introduce errors Ref2-4 Refactoring Tools IDEs offer some built-in refactorings NetBeans IDE has at least these refactorings see figure to right other IDEs may have more Most refactorings are NOT supported by IDEs Fowlers text has > 70 there are many, many more Ref2-5 So What? Programming methodologies based around refactorings Extreme Programming (XP) Agile Software Development natural part of maintaining or beautifying OO programs Refactorings automate common programming tasks that are tedious and error prone Design first, code later is alternative code evolves over time; connection of the code to the design slowly fades software decay code slowly sinks from engineering to hacking Refactoring is the opposite of this practice transforming bad code into better code can never transform bad code into great code. Ref2-6 Extreme Programming Idea to incrementally build your program using refactorings and extensions Kerievskys text is a practical example of what we talk about in this course how composing refactorings yield design patterns Ref2-7 Big Picture Refactorings with are supported by NetBeans Refactorings with are in Fowlers catalog Ref2-8 BASIC METHOD REFACTORINGS how individual methods evolve structurally Ref2-9 Refactoring This is a radically new way to think about refactorings but I think it is also a fundamental way (to implement refactoring engines) Think of a C function declaration not Java but C! and a call to this function: Ref2-10 int foo(B b, C c, D d) { /* body */ } foo(b1,c1,d1) Move Method Refactoring How would this function definition and call appear if foo was This is how to think about a method move refactoring when you move a method B.foo(C,D) to class D, it becomes D.foo(B,C) and all calls to foo are updated in the above way Q: from the above, what constraint would preclude the above move? Why is tool support necessary? Ref2-11 int foo(B b, C c, D d) { return b.x+c.x+d.x; } foo(b1,c1,d1) in classJava DeclarationJava thisJava call B D E unrelated in classJava DeclarationJava thisJava call Bint foo(C c, D d) { ret x+c.x+d.x } D E unrelated in classJava DeclarationJava thisJava call Bint foo(C c, D d) { ret x+c.x+d.x }b D E unrelated in classJava DeclarationJava thisJava call Bint foo(C c, D d) { ret x+c.x+d.x }bb1.foo(c1,d1) D E unrelated in classJava DeclarationJava thisJava call Bint foo(C c, D d) { ret x+c.x+d.x }bb1.foo(c1,d1) Dint foo(B b, C c) { ret b.x+c.x+x } E unrelated in classJava DeclarationJava thisJava call Bint foo(C c, D d) { ret x+c.x+d.x }bb1.foo(c1,d1) Dint foo(B b, C c) { ret b.x+c.x+x }d E unrelated in classJava DeclarationJava thisJava call Bint foo(C c, D d) { ret x+c.x+d.x }bb1.foo(c1,d1) Dint foo(B b, C c) { ret b.x+c.x+x }dd1.foo(b1,c1) E unrelated in classJava DeclarationJava thisJava call Bint foo(C c, D d) { ret x+c.x+d.x }bb1.foo(c1,d1) Dint foo(B b, C c) { ret b.x+c.x+x }dd1.foo(b1,c1) E unrelated static int foo(B b, C c, D d) { return b.x+c.x+d.x; } in classJava DeclarationJava thisJava call Bint foo(C c, D d) { ret x+c.x+d.x }bb1.foo(c1,d1) Dint foo(B b, C c) { ret b.x+c.x+x }dd1.foo(b1,c1) E unrelated static int foo(B b, C c, D d) { return b.x+c.x+d.x; } - in classJava DeclarationJava thisJava call Bint foo(C c, D d) { ret x+c.x+d.x }bb1.foo(c1,d1) Dint foo(B b, C c) { ret b.x+c.x+x }dd1.foo(b1,c1) E unrelated static int foo(B b, C c, D d) { return b.x+c.x+d.x; } -E.foo(b1,c1,d1) Move B.biff(A) A.biff(B) Here is the Eclipse project from which this video is taken you do it yourself Ref2-12 after move int foo(B b, C c, D d) { return b.x+c.x+d.x; }...foo(b1,c1,d1)... int foo(B b, C c, D d, A a) { return b.x+c.x+d.x; }...foo(b1,c1,d1,a1)... Add a parameter (A a) to foo( ) must supply a default argument a1 yields Remove an unused parameter (A a) from the above-modified foo( ) yields Change Method Signature Refactoring Ref2-13 int foo(B b, C c, D d) { return b.x+c.x+d.x; }...foo(b1,c1,d1)... Special case of remove unused parameter A is unused int foo(B b, C c, D d, A a) { return b.x+c.x+d.x; } Change Method Signature Ref2-14 int foo(B b, C c, D d) { return b.x+c.x+d.x; } Todays IDEs dont allow you to do this, but suppose foo is a method of class A After removing parameter A, the definition of foo should become Why? class A { int foo(B b, C c, D d) { return b.x+c.x+d.x; } } Corner Case of Change Method Signature Ref2-15 class A { static int foo(B b, C c, D d) { return b.x+c.x+d.x; } } see my answers in slide notes Another Refactoring: Move Via Field Move B.biff(A) C.biff(A,B) via field B.c Semantics of move via field is a sequence of refactorings add parameter C to biff with argument this.c move B.biff(A,C) C.biff(B,A) Ref2-16 Move via Field Refactoring Goal I want you to think as precisely as you can in software design it is not ad hoc Think of a refactoring engine being similar to reflection for each class in a program there is a RClass object method in a program there is an RMethod object field in a program there is a RField object So suppose rbiff is the RMethod object for method biff we know: Ref2-17 class B { C c; void biff(...) { }.. biff().. } RType t = rbiff.getReturnType();RType t = rbiff.getReturnType(); t.name.equals(void) // is true RType t = rbiff.getReturnType(); t.name.equals(void) // is true Rclass b = rbiff.getClass(); RType t = rbiff.getReturnType(); t.name.equals(void) // is true Rclass b = rbiff.getClass(); b.name.equals(B) // is true 1.verify that field f is a class and not a primitive type 2.add a new parameter to method m with name f.getName() with type f.getType() 3.move m to class f.getType() Move via Field is a Composite Refactoring Ref2-18 static void MoveViaField(RMethod m, RField f) { } static void MoveViaField(RMethod m, RField f) { RType ft = f.getType(); } static void MoveViaField(RMethod m, RField f) { RType ft = f.getType(); if (ft.isPrimitive()) { throw new RefactoringError(field cant be primitive); } } static void MoveViaField(RMethod m, RField f) { RType ft = f.getType(); if (ft.isPrimitive()) { throw new RefactoringError(field cant be primitive); } m.addParameter(f); // adds param fname of type ft } static void MoveViaField(RMethod m, RField f) { RType ft = f.getType(); if (ft.isPrimitive()) { throw new RefactoringError(field cant be primitive); } m.addParameter(f); // adds param fname of type ft m.moveTo(ft); } Rename Refactoring Rename changes the name of a method: foo bar rename all method calls as well Ref2-19 int foo(B b, C c, D d) { /* body1 */ } foo(b1,c1,d1) int bar(B b, C c, D d) { /* body1 */ } bar(b1,c1,d1) rename B.foo() to B.bar(A) Rename Refactoring There are constraints just as there are constraints in UML class diagrams for all refactorings Suppose I want to rename B.biff(A) to B.zoo(A) is this legal? Eclipse produces Ref2-20 Yes and No I think it is a bug! Cute, but not funny It is correct because the semantics of the program has not changed there is no call to B.biff(A) But look! Ref2-21 Strange What about this case? Rename B.biff(A) B.zoo(A) Eclipse does the right thing now by renaming all biff methods to zoo Because there is a reference Just be careful! Ref2-22 Substitute Refactoring Substitute changes the body of a method: body1 body2 to substitute semantically equivalent code fragments Example: Ref2-23 int find(String person) { if (person.equals(don)) return 1; if (person.equals(john) return 2; if (person.equals(sally) return 3; return 0; } int find(String person) { String[] array = {don,john,sally}; for (i=0; i