Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Recommending Automated Extract Method Refactorings (ICPC 2014)

Recommending Automated Extract Method Refactorings (ICPC 2014)

Extract Method is a key refactoring for improving program comprehension. However, recent empirical research shows that refactoring tools designed to automate Extract Methods are often underused. To tackle this issue, we propose a novel approach to identify and rank Extract Method refactoring opportunities that are directly automated by IDE-based refactoring tools. Our approach aims to recommend new methods that hide structural dependencies that are rarely used by the remaining statements in the original method. We conducted an exploratory study to experiment and define the best strategies to compute the dependencies and the similarity measures used by the proposed approach. We also evaluated our approach in a sample of 81 extract method opportunities generated for JUnit and JHotDraw, achieving a precision of 48% (JUnit) and 38% (JHotDraw).

ASERG, DCC, UFMG

June 02, 2014
Tweet

More Decks by ASERG, DCC, UFMG

Other Decks in Research

Transcript

  1. Danilo Silva Dept. of Computer Science UFMG, Brazil [email protected] Recommending

    Automated Extract Method Refactorings Ricardo Terra Dept. of Computer Science UFLA, Brazil [email protected] Marco Tulio Valente Dept. of Computer Science UFMG, Brazil [email protected] APPLIED SOFTWARE ENGINEERING RESEARCH GROUP /
  2. Introduction Extract Method Refactoring  Used in a wide range

    of scenarios  IDE-based automated support available, but underused 2
  3. Proposed Solution  Automated identification of Extract Method opportunities 3

    01 public String execute() throws Exception { 02 logger.info("Starting execute()"); 03 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 Transaction t = sess.beginTransaction(); 05 Criteria criteria = sess.createCriteria(User.class); 06 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 User user = (User) criteria.uniqueResult(); 09 t.commit(); 10 sess.close(); 11 if (user != null) { 12 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 logger.info("Finishing execute() -- Success"); 14 return SUCCESS; 15 } 16 this.addActionError(this.getText("login.failure")); 17 logger.info("Finishing execute() -- Failure"); 18 return INPUT; 19 }
  4. Proposed Solution JExtract  Generation of Extract Method candidates 

    Scoring function  Structural similarity between dependency sets  Filtering 4
  5. Generation of Candidates  Analysis of the AST to split

    the code into blocks  For each block  For each subsequence of statements  Check if it is a candidate for extraction 5
  6. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  7. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  8. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  9. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  10. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  11. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  12. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  13. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  14. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  15. Generation of Candidates  Moving window algorithm 6 01 public

    String execute() throws Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 }
  16. Scoring Function  Variables 8 01 public String execute() throws

    Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 } Dependency on variable criteria
  17. Scoring Function  Types 9 01 public String execute() throws

    Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 } Method of type Criteria invoked Dependency on type Criteria
  18. Scoring Function  Packages 10 01 public String execute() throws

    Exception { 02 1/01 logger.info("Starting execute()"); 03 1/02 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 1/03 Transaction t = sess.beginTransaction(); 05 1/04 Criteria criteria = sess.createCriteria(User.class); 06 1/05 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 1/06 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 1/07 User user = (User) criteria.uniqueResult(); 09 1/08 t.commit(); 10 1/09 sess.close(); 11 1/10 if (user != null) { 12 2/01 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 2/02 logger.info("Finishing execute() -- Success"); 14 2/03 return SUCCESS; 15 } 16 1/11 this.addActionError(this.getText("login.failure")); 17 1/12 logger.info("Finishing execute() -- Failure"); 18 1/13 return INPUT; 19 } Dependency on package org.hibernate (where Criteria belongs to)
  19. Scoring Function  Two dependency sets:  The code fragment

    to be extracted Dep  The remaining statements Dep’  Dep should not be similar to Dep’  Extracted fragment should hide complexity 11 Dep’ (Remaining) Dep (Extracted) Good Bad Dep’ (Remaining) Dep (Extracted)
  20. Case study: MyWebMarket  Small Web application  25 well-known

    Extract Method instances  14 of then supported by IDEs 13 Approach #Recom. #Found Recall Prec. JExtract (top-1) 14 12 85.7% 85.7% JExtract (top-2) 28 14 100.0% 50.0% JExtract (top-3) 42 14 100.0% 33.3% JDeodorant 20 2 14.3% 10.0%
  21. Case study: JUnit and JHotDraw  Well-known opportunities introduced by

    inlining methods  25 in JUnit  56 in JHotDraw 14 System Approach #Reco. #Found Recall Prec. JUnit (25 known opport.) JExtract (top-1) 25 12 48.0% 48.0% JExtract (top-2) 29 16 64.0% 32.7% JExtract (top-3) 72 18 72.0% 25.0% JDeodorant 39 0 0.0% 0.0% JHotDraw (56 known opport.) JExtract (top-1) 56 21 37.5% 37.5% JExtract (top-2) 110 28 50.0% 25.5% JExtract (top-3) 162 33 58.9% 20.4% JDeodorant 67 4 7.1% 6.0%
  22. Future Work  New evaluation with developers  Statement reordering

    before extracting the code fragment  Combine Extract Method with Move Method 15
  23. Extra slide  Some of JDeodorant’s recommendations incorrectly reordered statements

    involving database transactions 17 01 public String execute() throws Exception { 02 logger.info("Starting execute()"); 03 Session sess = HibernateUtil.getSessionFactory().openSession(); 04 Transaction t = sess.beginTransaction(); 05 Criteria criteria = sess.createCriteria(User.class); 06 criteria.add(Restrictions.idEq(this.user.getUsername())); 07 criteria.add(Restrictions.eq("password", this.user.getPassword())); 08 User user = (User) criteria.uniqueResult(); 09 t.commit(); 10 sess.close(); 11 if (user != null) { 12 ActionContext.getContext().getSession().put(AUTHENTICATED_USER, user); 13 logger.info("Finishing execute() -- Success"); 14 return SUCCESS; 15 } 16 this.addActionError(this.getText("login.failure")); 17 logger.info("Finishing execute() -- Failure"); 18 return INPUT; 19 }