On the separation of queries from modifiers Ran

  • Slides: 15
Download presentation
On the separation of queries from modifiers Ran Ettinger, IBM Research – Haifa COST

On the separation of queries from modifiers Ran Ettinger, IBM Research – Haifa COST Action IC 0701 9 th MC and WG Meeting Darmstadt, Germany 29 February 2012 1

Separate Query from Modifier (SQf. M) • A refactoring technique by Martin Fowler* –

Separate Query from Modifier (SQf. M) • A refactoring technique by Martin Fowler* – “You have a method that returns a value but also changes the state of an object. ” – “Create two methods, one for the query and one for the modification. ” • • Inspired by Bertrand Meyer’s Command Query Separation (CQS) This talk: – – Outline of a first algorithm to support the automation of this refactoring Based on program slicing and sliding, with reference to other refactoring techniques A prototype tool integrated into Eclipse Open source implementation in WALA (http: //wala. sourceforge. net) • • Developed by Alex Libov, Eli Kfir and Daniel Lemel (Technion, Israel Institute of Technology) Contributions by Dima Rabkin and Vlad Shumlin (Haifa University) Further investigation in Hemachandran Kalaimani’s MSc thesis (Leicester University) Based on a slicer for Java by Stephen J. Fink (IBM Research) and the WALA contributors * See http: //www. refactoring. com/catalog/separate. Query. From. Modifier. html and http: //sourcemaking. com/refactoring/separate-query-from-modifier 2

Fowler’s Example (Before SQf. M) String found. Miscreant(String[] people) { for (int i=0; i<people.

Fowler’s Example (Before SQf. M) String found. Miscreant(String[] people) { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { send. Alert(); return "Don"; } if (people[i]. equals("John")) { send. Alert(); return "John"; } } return ""; } void check. Security(String[] people) { String found = found. Miscreant(people); some. Later. Code(found); } 3

Fowler’s Example (After SQf. M) String found. Person(String[] people) { for (int i=0; i<people.

Fowler’s Example (After SQf. M) String found. Person(String[] people) { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { return "Don"; } if (people[i]. equals("John")) { return "John"; } void send. Alert(String[] people) { } for (int i=0; i<people. length; i++) { return ""; if (people[i]. equals("Don")) { } send. Alert(); return; } if (people[i]. equals("John")) { send. Alert(); return; } } } void check. Security(String[] people) { send. Alert(people); String found = found. Person(people); some. Later. Code(found); } 4

Fowler’s Example (Beyond SQf. M) String found. Person(String[] people) { for (int i=0; i<people.

Fowler’s Example (Beyond SQf. M) String found. Person(String[] people) { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { return "Don"; } if (people[i]. equals("John")) { return "John"; } } return ""; } void send. Alert(String[] people) { if (!found. Person(people). equals("")) send. Alert(); } void check. Security(String[] people) { send. Alert(people); String found = found. Person(people); some. Later. Code(found); } 5

Proposed Mechanics for SQf. M 1. Prepare the method for sliding by ensuring it

Proposed Mechanics for SQf. M 1. Prepare the method for sliding by ensuring it has a single return statement. • 2. 3. 4. Compile and test. Perform sliding on the returned value. Perform Extract Method on the slice (Q). [Optional] Perform Inline Temp on the result of Q. • 5. 6. Compile and test. Perform Extract Method on the co-slice (M). Undo the preparatory step (in Q and M). • 7. Compile and test. Perform Inline Method on the refactored version of the selected method. • Compile and test. 6

Step 1: Single Return String found. Miscreant(String[] people) { String result; body: { for

Step 1: Single Return String found. Miscreant(String[] people) { String result; body: { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { send. Alert(); result = "Don"; break body; } if (people[i]. equals("John")) { send. Alert(); result = "John"; break body; } } result = ""; } return result; } 7

Step 2: Sliding (of result) String found. Miscreant(String[] people) { String result; slice: {

Step 2: Sliding (of result) String found. Miscreant(String[] people) { String result; slice: { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { result = "Don"; break slice; } if (people[i]. equals("John")) { result = "John"; break slice; } } result = ""; } co_slice: { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { send. Alert(); break co_slice; } if (people[i]. equals("John")) { send. Alert(); break co_slice; } } } return result; } 8

Step 3: Extract Method (Q( String found. Person(String[] people) { String result; slice: {

Step 3: Extract Method (Q( String found. Person(String[] people) { String result; slice: { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { result = "Don"; break slice; } if (people[i]. equals("John")) { String found. Miscreant(String[] people) { result = "John"; String result = found. Person(people); break slice; co_slice: { } for (int i=0; i<people. length; i++) { } if (people[i]. equals("Don")) { result = ""; send. Alert(); } break co_slice; return result; } } if (people[i]. equals("John")) { send. Alert(); break co_slice; } } } return result; } 9

Step 4: Inline Temp (result) String found. Person(String[] people) { String result; slice: {

Step 4: Inline Temp (result) String found. Person(String[] people) { String result; slice: { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { result = "Don"; break slice; } if (people[i]. equals("John")) { result = "John"; String found. Miscreant(String[] people) { break slice; co_slice: { } for (int i=0; i<people. length; i++) { } if (people[i]. equals("Don")) { result = ""; send. Alert(); } break co_slice; return result; } } if (people[i]. equals("John")) { send. Alert(); break co_slice; } } } return found. Person(people); } 10

Step 5: Extract Method (M) String found. Person(String[] people) { void send. Alert(String[] people)

Step 5: Extract Method (M) String found. Person(String[] people) { void send. Alert(String[] people) { String result; co_slice: { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { send. Alert(); result = "Don"; break co_slice; break slice; } } if (people[i]. equals("John")) { send. Alert(); result = "John"; break co_slice; break slice; } } } result = ""; } } return result; String found. Miscreant(String[] people) { } send. Alert(); return found. Person(people); } void check. Security(String[] people) { String found = found. Miscreant(people); some. Later. Code(found); } 11

Step 6: Undo Step 1 (in Q and M) String found. Person(String[] people) {

Step 6: Undo Step 1 (in Q and M) String found. Person(String[] people) { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { return "Don"; } if (people[i]. equals("John")) { return "John"; } } return ""; } void send. Alert(String[] people) { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { send. Alert(); return; } if (people[i]. equals("John")) { send. Alert(); return; } } } String found. Miscreant(String[] people) { send. Alert(); return found. Person(people); } void check. Security(String[] people) { String found = found. Miscreant(people); some. Later. Code(found); } 12

Step 7: Inline Method String found. Person(String[] people) { for (int i=0; i<people. length;

Step 7: Inline Method String found. Person(String[] people) { for (int i=0; i<people. length; i++) { if (people[i]. equals("Don")) { return "Don"; } if (people[i]. equals("John")) { return "John"; } void send. Alert(String[] people) { } for (int i=0; i<people. length; i++) { return ""; if (people[i]. equals("Don")) { } send. Alert(); return; } if (people[i]. equals("John")) { send. Alert(); return; } } } void check. Security(String[] people) { send. Alert(people); String found = found. Person(people); some. Later. Code(found); } 13

Conditions for Behavior Preservation • The two new method names must be legal and

Conditions for Behavior Preservation • The two new method names must be legal and cause no conflict • The code of Q must be free of side effects – Otherwise, can some measures be taken to prevent the effects? – Further SQf. M of called methods might be needed, requiring further user interaction • Legal selection of a method – It should be non-void and with side effects (or M would be empty) – If it participates in overriding special treatment is needed • Example: A Java Iterator’s next() method 14

Some Challenges • How not to fail when the Query has side effects –

Some Challenges • How not to fail when the Query has side effects – Idea: assuming Q will follow M, try to reuse some of M’s results in Q instead of re-computing them; so it is the slice of the side effects that will be extracted, instead of that of the returned value • How to minimize code duplication, correctly – which extraction technique (of Q or of M) should be preferred? • How not to fail in the final (Inline Method) step – When the call is inside a loop’s condition the Modifier’s invocation location is non-trivial – The Eclipse “Inline” treatment needs improvement 15