[Refactoring] 객체 간 기능 이동

말하는 감자·2022년 4월 15일
0

Refactoring

목록 보기
4/9
post-thumbnail

📌 객체 간 기능 이동 (Moving Features between Objects)

클래스 간에 기능을 안전하게 이동하고 새 클래스를 만들고 구현 세부 정보를 외부 접근으로부터 숨길 수 있는 방법


✏️ 메소드 이동 (Move Method)

메소드가 자체 클래스보다 다른 클래스에서 더 많이 사용된다면 가장 많이 사용되는 클래스로 메소드 이동

✔️ 장점

  • 클래스를 내부적으로 일관성 있게 만듬

  • 클래스 간의 종속성 감소


✏️ 필드 이동 (Move Field)

필드가 자신의 클래스보다 다른 클래스에서 더 많이 사용될 경우 더 많이 사용되는 클래스로 필드 이동


✏️ 클래스 추출 (Extract Class)

한 클래스가 여러 작업을 한다면 새 클래스를 만들고 관련 기능을 담당하는 필드와 메소드를 해당 클래스에 배치

✔️ 장점

  • 단일 책임 원칙을 준수

  • 클래스의 코드가 더 명확하고 이해하기 쉬움

  • 안정적이고 다른 클래스의 변경 사항에 영항을 많이 받지 않음

    단일 책임 원칙(Single Responsibility Principle)

    모든 클래스는 하나의 책임만 가져야 한다.

❌ 단점

  • 과도하게 사용시 클래스 인라인(Inline Class)해야함

✏️ 클래스 인라인 (Inline Class)

클래스가 거의 아무런 기능을 하지 않을 정도로 역할이 작다면 다른 클래스로 모든 기능을 이동

✔️ 장점

  • 불필요한 클래스를 제거되며 컴퓨터의 운영 메모리 확보

✏️ 대리 객체 숨기기 (Hide Delegate)

클라이언트가 객체의 위임 클래스를 직접 호출하고 있는 경우 서버에 메서드를 만들어서 대리 객체를 숨김

📃 예제

<변경 전>

class Person {
  Department _department;
  public Department getDepartment() {
    return _department;
  }
  public void setDepartment(Department arg) {
    _department = arg;
  }
}

class Department {
  private String _chargeCode;
  private Person _manager;
  public Department (Person manager) {
    _manager = manager;
  }
  public Person getManager() {
    return _manager;
  }
}

클라이언트가 어떤 사람의 매니저를 알려고 한다면 먼저 그 사람이 속해 있는 부서를 알아야 한다.

manager = john.getDepartment().getManager();

Department 클래스가 매니저 정보를 알려주는 메소드가 있다는 것을 클라이언트가 알게 된다.
Person 클래스에 위임 메소드를 만들어서 Department의 존재를 클라이언트에게 숨기면 결합을 줄일 수 있다.
또한 getDepartment() 접근자를 제거할 수 있다.
(Person 클래스가 부서 등록과 매니저 조회만 한다는 조건 하에)

<변경 후>

class Person {
  Department _department;
  public void setDepartment(Department arg) {
    _department = arg;
  }
  public Person getManager() {
  	return _department.getManager();
  }
}

class Department {
  private String _chargeCode;
  private Person _manager;
  public Department (Person manager) {
    _manager = manager;
  }
  public Person getManager() {
    return _manager;
  }
}

모든 클라이언트가 이 Person 클래스의 새로운 메소드를 사용하게 한다.

manager = john.getManager();

✔️ 장점

  • 클라이언트 코드가 객체간 관계의 세부 사항에 대해 아는 내용이 적어지면 프로그램의 변경이 더 쉬워짐

❌ 단점

  • 너무 많은 위임 메소드를 생성하면 불필요한 대리 객체가 생성됨

✏️ 외부 메소드 도입 (Introduce Foreign Method)

서버 클래스에 필요한 메소드가 포함되어 있지 않고 클래스에 메소드를 추가할 수 없다면 클라이언트 클래스에 메소드를 추가하고 서버 클래스의 인스턴스를 인수로 전달

<변경 전>

class Report {
  // ...
  void sendReport() {
    Date nextDay = new Date(previousEnd.getYear(),
      previousEnd.getMonth(), previousEnd.getDate() + 1);
    // ...
  }
}

<변경 후>

class Report {
  // ...
  void sendReport() {
    Date newStart = nextDay(previousEnd);
    // ...
  }
  private static Date nextDay(Date arg) {
    return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
  }
}

✔️ 장점

  • 코드 중복 제거

✏️ 지역 확장 도입 (Introduce Local Extension)

서버 클래스에 필요한 메소드가 포함되어 있지 않고 클래스에 메소드를 추가할 수 없다면 메소드를 포함하는 새 클래스를 만들고 원래 클래스의 자식 클래스 또는 래퍼(wrapper) 클래스로 만듬

📃 예제

<자식 클래스>

class MfDateSub extends Date {
 // 생성자 정의
  public MfDateSub (String dateString) {
    super(dateString);
  }
// 변환 생성자 정의
  public MfDateSub (Date arg) {
    super(arg.getTime())
  }
//필요한 외래 메소드 정의
  Date nextDay() {
    return new Date (getYear(), getMonth(), getDate() + 1);
  }
}

<래퍼 클래스>

class MfDateWrap {
  private Date _original;
//생성자 정의
  public MfDateWrap(String dateString) {
    _original = new Date(dateString);
  }
//변환 생성자 정의
  public MfDateWrap(Date arg) {
    _original = arg;
  }
//기존 기능 위임 메소드
  public int getYear() {
    return _original.getYear();
  }
//기존 기능 위임 메소드
  public boolean equals (MfDateWrap arg) {
    return (toDate().equals(arg.toDate()));
  }
//필요한 외래 메소드 정의
  Date nextDay() {
    return new Date (getYear(), getMonth(), getDate() + 1);
  }
}

✔️ 장점

  • 추가 메소드를 별도의 확장 클래스(자식 or 래퍼)로 이동하면 적합하지 않은 코드로 클라이언트 클래스와 묶여지는 것을 예방

  • 프로그램 구성 요소는 더 일관성 있고 재사용 가능

❓ Foreign Method & Local Extension

소스 코드를 수정할 수 없는 경우에
한두 개의 메소드 → Foreign Method
자식 클래스나 래퍼 클래스 → Local Extension


📑 참고 자료

profile
나는 말하는 감자다

0개의 댓글