Slide 1

Slide 1 text

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 /

Slide 2

Slide 2 text

Introduction Extract Method Refactoring  Used in a wide range of scenarios  IDE-based automated support available, but underused 2

Slide 3

Slide 3 text

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 }

Slide 4

Slide 4 text

Proposed Solution JExtract  Generation of Extract Method candidates  Scoring function  Structural similarity between dependency sets  Filtering 4

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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 }

Slide 7

Slide 7 text

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 }

Slide 8

Slide 8 text

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 }

Slide 9

Slide 9 text

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 }

Slide 10

Slide 10 text

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 }

Slide 11

Slide 11 text

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 }

Slide 12

Slide 12 text

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 }

Slide 13

Slide 13 text

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 }

Slide 14

Slide 14 text

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 }

Slide 15

Slide 15 text

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 }

Slide 16

Slide 16 text

Scoring Function  Structural dependencies  Three levels:  Variables  Types  Packages 7

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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)

Slide 20

Slide 20 text

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)

Slide 21

Slide 21 text

Scoring Function 12  Distance using Kulczynski set similarity coefficient  Final scoring function

Slide 22

Slide 22 text

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%

Slide 23

Slide 23 text

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%

Slide 24

Slide 24 text

Future Work  New evaluation with developers  Statement reordering before extracting the code fragment  Combine Extract Method with Move Method 15

Slide 25

Slide 25 text

Thank you 16 APPLIED SOFTWARE ENGINEERING RESEARCH GROUP /

Slide 26

Slide 26 text

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 }