끄적끄적

리팩토링 7장 - 객체간의 기능 이동 본문

리팩토링

리팩토링 7장 - 객체간의 기능 이동

widruv 2016. 3. 12. 13:47
책임을 어디에 둘 것인가

  • Move Field
  • Move Method
  • Extract Class
  • Inline Class

Move Method(v)
  • 메소드가 자신이 정의된 클래스보다 다른 클래스의 기능을 더 많이 사용하고 있다면
  • 이 메소드를 가장 많이 사용하고 있는 클래스에 비슷한 몸체를 가진 새로운 메소드를 만들어라. 
  • 그리고 이 전 메소드는 간단한 위임으로 바꾸거나 완전히 제거하라.
  • 동기
    • 자신이 속해 있는 클래스보다 다른 클래스를 더 많이 참조하는 메소드가 있는지 확인
  • 절차
    • 소스 클래스에 정의된 소스 메소드에 의해 사용되는 모든 부분을 조사
      • 어떤 부분이 지금 옮기려는 메소드에서만 사용된다면, 그 부분 또한 같이 옮기는 것이 낫다.
    • 소스 클래스의 서브클래스나 슈퍼클래스에서 옮기려고 하는 메소드에 대한 다른 선언이 있는지 확인
    • 타겟 클래스에 메소드를 정의
    • 소스 메소드에서 타겟 메소드로 코드를 복사. 그리고 그 메소드가 타겟 클래스에서 동작하도록 적절히 수정
    • 타겟 클래스 컴파일
    • 소스 클래스에서 적절한 타겟 객체를 참조하는 방법 결정
    • 소스 메소드를 위임 메소드로 바꾼다.
    • 컴파일, 테스트 한다.
    • 소스 메소드를 제거할지 위임 메소드로 남겨둘지 결정
      • 만약 소스 메소드를 참조하는 부분이 많다면, 소스 메소드를 위임 메소드로 남겨두는 것이 더 쉬운 방법
      • 소스 메소드를 제거한다면 소스 메소드를 참조하고 있는 부분을 타겟 메소드를 참조하도록 수정
    • 컴파일, 테스트

Move Field(v)
  • 필드가 자신이 정의된 클래스보다 다른 클래스에 의해서 더 많이 사용되고 있다면
  • 타겟 클래스에 새로운 필드를 만들고 기존 필드를 사용하고 있는 모든 부분을 변경하라.
  • 동기
    • 어떤 Field가 자신이 속한 Class보다 다른 Class의 Method에서 더 많이 사용되고 있을 때
    • get/set으로 다른 곳에서 많이 쓰고 있을 거 같으면 Method를 옮기는 게 좋다.
    • 그러나 Method의 위치가 적절하다면 필드를 옮긴다.
    • Extract Class를 할 땐 Field를 먼저 옮기고 Method를 옮긴다.
  • 절차
    • Field가 public으로 선언되어 있으면 Encapsulate Field를 사용
    • Compile, Test
    • Target Class에 Field와 그 Field에 대한 get/set method를 만든다.
    • Compile Target Class 
    • Source Class에서 Target 객체를 참조하는 방법 결정
    • Source Class에 있는 Field 제거
    • Source Field를 참조하고 있는 모든 부분을 Target Class에 있는 적당한 Method를 참조하도록 바꾼다.
    • Compile, Test

Extract Class(v)
  • 두 개의 Class가 해야 할 일을 하나의 Class가 하고 있는 경우
  • 새로운 Class를 만들어서 관련 있는 Field와 Method를 예전 Class에서 새로운 Class로 옮겨라
  • 동기
    • Class가 많은 Method, Data를 가지고 있고, 너무 커서 쉽게 이해하기 어려운 경우
    • 어떤 Data의 부분 집합과 Method의 부분 집합이 같이 몰려다니는 경우
    • 만약 일부 Data나 Method를 제거한다면 다른 Field나 Method가 의미 없는 것이 될지를 물어보는 것은 편리한 테스트 방법
      • 그렇다면 서로 연관있는 것일테니까
  • 절차
    • Class의 책임을 어떻게 나눌지 결정
    • 분리된 책임을 떠맡을 새로운 Class 생성
      • 책임을 분리한 후 이전 Class의 책임이 이름과 더 이상 맞지 않는다면, 이전 클래스의 이름도 변경
    • 이전 클래스에서 새로 만든 클래스에 대한 링크를 만든다.
      • 양방향 링크가 필요할지 모른다.
      • 하지만 필요해지기 전에는 새로 만든 클래스에서 이전 클래스로 가는 링크를 만들지 말라.
    • 옮기고자 하는 각각의 Field에 대해 Move Field 사용
    • 각각의 Field를 옮길 때마다 Compile, Test
    • Move Method를 사용해서 이전 Class에서 새로 만든 Class로 Method를 옮긴다.
    • 저수준 Method(호출하기보다 호출되는 Method) -> 고수준 Method 순서로
    • 각각의 Method 옮길 때마다 Compile, Test
    • 각 Class를 검토하고, Interface를 줄인다.
      • 양방향 링크를 가지고 있다면, 단방향 링크로 만들 수 있는지 확인
    • 새로운 Class를 공개할지 결정

Inline Class
  • Class가 하는 일이 많지 않은 경우에는
  • 그 Class에 있는 모든 변수와 메소드를 다른 클래스로 옮기고 그 클래스를 제거하라.
  • 동기
    • Extract Class와 반대
    • Refactoring으로 그 Class의 책임을 대부분 다른 곳으로 옮기고 남은 것이 거의 없는 경우
  • 절차
    • 흡수하는 Class에 Source Class의 public Field와 Method를 선언
    • Source Class를 참조하고 있는 모든 부분을 흡수하는 Class를 참조하도록 변경
    • Compile, Test
    • Move Method와 Move Field를 사용하여 Source Class에 있는 모든 Field와 Method를 흡수하는 Class로 옮김

Hide Delegate
  • Client가 객체의 위임 Class를 직접 호출하고 있는 경우
  • 서버에 Method를 만들어서 대리객체(delegate)를 숨겨라

Remove Middle Man
  • Class가 간단한 위임을 너무 많이 하고 있는 경우에는
  • Client가 대리객체(delegate)를 직접 호출하도록 하라.

Introduce Foreign Method
  • 사용하고 있는 서버 Class에 부가적인 Method가 필요하지만 Class를 수정할 수 없는 경우에는 
  • 첫 번째 인자로 서버 Class의 인스턴스를 받는 Method를 클라이언트에 만들어라
  • 동기
    • 모든 서비스를 제공하는 정말로 멋진 Class를 사용하고 있다.
    • 그러나 꼭 필요하지만 그 Class가 제공하지 않는 서비스가 하나 있다. 
    • 그런데 소스 코드를 변경할 수 없다면 클라이언트에서 외래 메소드(Foreign Method)로 만들어주는 방법밖에 없다. 
    • Foreign Method는 임시방편일 뿐
  • 절차
    • 필요한 작업을 하는 Method를 클라이언트 Class에 만든다.
      • 이 Method는 클라이언트 Class의 어떤 부분에도 접근해선 안 됨
      • 값이 필요하다면 값을 Parameter로 넘겨야 함
    • 첫 번째 Parameter로 서버 Class의 인스턴스를 받도록 함
    • Method에 "외래 메소드, 원래 서버 클래스에 있어야 한다."와 같은 주석을 달아 놓는다.

Introduce Local Extension(v)

  • 사용하고 있는 서버 Class에 여러 개의 Method를 추가할 필요가 있지만 서버 Class를 수정할 수 없는 경우
  • 필요한 추가 Method를 포함하는 새로운 Class를 만들어라.
  • 이 확장 Class를 원래 Class의 서브클래스 또는 래퍼 클래스로 만들어라.
  • 동기
    • Introduce Foreign Method만으로는 감당이 안 될 만큼 Method가 많은 경우


Comments