리팩토링 - 냄새 12, 13, 14, 15, 16

김상운(개발둥이)·2022년 4월 8일
1

리팩토링

목록 보기
13/17
post-thumbnail

들어가기

해당 포스팅은 인프런 백기선님의 '리팩토링'을 학습 후 정리한 내용입니다.



냄새 12. 반복되는 switch 문

Repeated Swtiches

  • 예전에는 switch 문이 한번만 등장해도 코드 냄새로 생각하고 다형성 적용을 권장했다.
  • 하지만 최근에는 다형성이 꽤 널리 사용되고 있으며, 여러 프로그래밍 언어에서 보다 세련
    된 형태의 switch 문을 지원하고 있다.
  • 따라서 오늘날은“반복해서 등장하는 동일한 switch 문”을 냄새로 여기고 있다.
  • 반복해서 동일한 switch 문이 존재할 경우, 새로운 조건을 추가하거나 기존의 조건을 변경
    할 때모든 switch 문을 찾아서 코드를 고쳐야 할지도 모른다


냄새 13. 반복문

Loops

  • 프로그래밍 언어 초기부터 있었던 반복문은 처음엔 별다른 대안이 없어서 간과했지만 최근 Java와 같은 언어에서 함수형 프로그래밍을 지원하면서 반복문에 비해 더 나은 대안책이 생겼다.
  • 반복문을 파이프라인으로 바꾸는 (Replace Loop with Pipeline)” 리팩토링을 적용하면 필터나 맵핑과 같은 파이프라인 기능을 사용해 보다 빠르게 어떤 작업을 하는지 파악할 수
    있다.

반복문을 파이프라인으로 바꾸기

Replace Loop with Pipeline

  • 콜렉션 파이프라인 (자바의 Stream, C#의 LINQ - Language Integrated Query)
  • 고전적인 반복문을 파이프라인 오퍼레이션을 사용해 표현하면 코드를 더 명확하게 만들 수있다.
    • 필터 (filter): 전달받은 조건의 true에 해당하는 데이터만 다음 오퍼레이션으로 전달.
    • 맵 (map): 전달받은 함수를 사용해 입력값을 원하는 출력값으로 변환하여 다음 오퍼레이션으로 전달.
      https://martinfowler.com/articles/refactoring-pipelines.html

예제 코드

public class Author {

    private String company;

    private String twitterHandle;

    public Author(String company, String twitterHandle) {
        this.company = company;
        this.twitterHandle = twitterHandle;
    }

    static public List<String> TwitterHandles(List<Author> authors, String company) {
        var result = new ArrayList<String> ();
        for (Author a : authors) {
            if (a.company.equals(company)) {
                var handle = a.twitterHandle;
                if (handle != null)
                    result.add(handle);
            }
        }
        return result;
    }

}

냄새

반복문을 사용하는데 있어서 복잡한 로직이 있어 명확하게 파악하기 힘들다.

해결

파이프라인을 만들어 해결하자!

리팩토링 후

public class Author {

    private String company;

    private String twitterHandle;

    public Author(String company, String twitterHandle) {
        this.company = company;
        this.twitterHandle = twitterHandle;
    }

    public static List<String> TwitterHandles(List<Author> authors, String company) {

        return authors.stream().filter(author -> author.equals(company))
                .map(author -> author.twitterHandle)
                .filter(t -> t != null)
                .collect(Collectors.toList());
    }

}

설명

java 8 부터 지원하는 stream 을 사용하여 filter, map 등을 사용하여 반복문을 보다 명확하게 표현하였다.



냄새 14. 성의없는 요소

Lazy Element

  • 여러 프로그래밍적인 요소(변수, 메소드, 클래스 등)를 만드는 이유
    • 나중에 발생할 변화를 대비해서…
    • 해당 함수 또는 클래스를 재사용하려고…
    • 의미있는 이름을 지어주려고…
  • 가끔 그렇게 예상하고 만들어 놓은 요소들이 기대에 부응하지 못하는 경우가 있는데 그런 경우에 해당 요소들을 제거해야 한다.
  • 관련 리팩토링 기술
    • “함수 인라인 (Inline Function)”
    • “클래스 인라인 (Inline Class)”
    • 불필요한 상속 구조는 “계층 합치기 (Collapse Hierarchy)”를 사용할 수 있다

계층 합치기

Collapse Hierarchy

  • 상속 구조를 리팩토링하는 중에 기능을 올리고 내리다 보면 하위클래스와 상위클래스 코드에 차이가 없는 경우가 발생할 수 있다. 그런 경우에 그 둘을 합칠 수 있다.
  • 하위클래스와 상위클래스 중에 어떤 것을 없애야 하는가? (둘 중에 보다 이름이 적절한 쪽을 선택하지만, 애매하다면 어느쪽을 선택해도 문제없다.)

해결

pull members up, pull members down 을 사용하여 상위 클래스로 올리고 하위 클래스로 필드, 메서드를 옮길 수 있다.



냄새 15. 추측성 일반화

Speculative Generality

  • 나중에 이러 저러한 기능이 생길 것으로 예상하여, 여러 경우에 필요로 할만한 기능을 만들어 놨지만 “그런 일은 없었고…”결국에 쓰이지 않는 코드가 발생한 경우.
  • XP의 YAGNI (You aren’t gonna need it) 원칙을 따르자.
  • 관련 리팩토링
    • 추상 클래스를 만들었지만 크게 유요하지 않다면 “계층 합치기 (Collapse Hierarchy)”
    • 불필요한 위임은 “함수 인라인 (Inline Function)” 또는 “클래스 인라인 (Inline Class)”
    • 사용하지 않는 매개변수를 가진 함수는 “함수 선언 변경하기 (Change Function Declaration)”
    • 오로지 테스트 코드에서만 사용하고 있는 코드는 “죽은 코드 제거하기 (Remove Dead Code)”

죽은 코드 제거하기

Remove Dead Code

  • 사용하지 않는 코드가 애플리케이션 성능이나 기능에 영향을 끼치지는 않는다.
  • 하지만, 해당 소프트웨어가 어떻게 동작하는지 이해하려는 사람들에게는 꽤 고통을 줄 수
    있다.
  • 실제로 나중에 필요해질 코드라 하더라도 지금 쓰이지 않는 코드라면 (주석으로 감싸는게
    아니라) 삭제해야 한다.
  • 나중에 정말로 다시 필요해진다면 git과 같은 버전 관리 시스템을 사용해 복원할 수 있
    다.

냄새 16. 임시 필드

Temporary Field

  • 클래스에 있는 어떤 필드가 특정한 경우에만 값을 갖는 경우.
  • 어떤 객체의 필드가 “특정한 경우에만” 값을 가진다는 것을 이해하는 것은 일반적으로 예상하지 못하기 때문에 이해하기 어렵다.
  • 관련 리팩토링
    • “클래스 추출하기 (Extract Class)”를 사용해 해당 변수들을 옮길 수 있다.
    • “함수 옮기기 (Move Function)”을 사용해서 해당 변수를 사용하는 함수를 특정 클래스로 옮길 수 있다.
    • “특이 케이스 추가하기 (Introduce Special Case)”를 적용해 “특정한 경우”에 해당하는 클래스를 만들어 해당 조건을 제거할 수 있다.
profile
공부한 것을 잊지 않기 위해, 고민했던 흔적을 남겨 성장하기 위해 글을 씁니다.

0개의 댓글