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
    /

    View Slide

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

    View Slide

  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 }

    View Slide

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

    View Slide

  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

    View Slide

  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 }

    View Slide

  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 }

    View Slide

  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 }

    View Slide

  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 }

    View Slide

  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 }

    View Slide

  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 }

    View Slide

  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 }

    View Slide

  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 }

    View Slide

  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 }

    View Slide

  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 }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. Thank you
    16
    APPLIED SOFTWARE ENGINEERING
    RESEARCH GROUP
    /

    View Slide

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

    View Slide