메서드 이동

메서드가 자신이 속한 클래스보다 다른 클래스의 기능을 더 많이 이용할 땐
그 메서드가 제일 많이 이용하는 클래스 안에서 비슷한 내용의 새 메서드를 작성하자
기존 메서드는 간단히 대리(위임) 메서드로 전환하든지 아예 삭제하자

동기

  • 클래스에 기능이 너무 많거나 클래스가 다른 클래스와 과하게 연동되어 의존성이 지나칠 경우에는 메서드를 옮기는 것이 좋다
  • 메서드가 자신이 속한 객체보다 다른 객체를 더 많이 참조하는 경우에도 메서드를 옮기는 것이 좋다
  • 옮길만한 메서드가 발견되면 그 메서드 호출하는 메서드, 그 메서드 호출하는 메서드, 상속 계층에서 그 메서드를 재정의하는 메서드를 살펴본다
  • 판단이 힘들다면 직감에 따라 판단하고, 나중에 판단을 변경해도 된다

방법

  1. 원본 클래스의 원본 메서드에 사용된 모든 기능을 검사해서 그 기능들도 전부 옮겨야할지 판단한다
    • 옮길 메서드에만 사용되는 기능일 경우 그 메서드와 함께 옮겨야한다
  2. 원본 클래스의 하위 클래스와 상위 클래스에서 그 메서드에 대한 다른 선언이 있는지 검사하자
    • 오버라이딩 등
  3. 그 메서드를 대상 클래스안에 선언하고, 원본 메서드의 코드를 복사하고, 잘 돌아가게끔 수정한다
    • 메서드 이름을 적절히 바꿔도 된다
    • 원본 클래스의 변수를 사용한다면 매개변수로 던져주도록 한다
    • 옮길 원본 클래스내에 원본 클래스의 다른 메서드가 들어있거나 원본 클래스의 변수를 2개 이상 사용한다면, 원본 클래스를 매개변수로 던진다
      • 근데 이 상태면 메서드를 이동하는게 맞는지 다시 한번 생각해봐야한다
    • 예외처리 코드가 들어있다면 예외를 논리적으로 어느 클래스가 처리할 지 정해야한다
  4. 원본 클래스에서 옮겨진 대상 클래스 메서드를 참조할 방법을 정한다
    • 원본 메서드를 삭제한다
      • 찾아바꾸기 기능을 사용하면 빠르게 할 수 있다
    • 위임 메서드를 사용한다
      • 참조가 많을땐 이 방법이 더 편하다

예시

아래에서 moveMethod를 B 클래스로 옮겨야 하는 상황이라고 가정한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A {
private int someVar;
private B b;

public void moveMethod() {
if(b.someCondition()) {
// do something
someVar;
// do something
}
}

public void someMethod() {
// do something
moveMethod();
// do something
}
}

moveMethod를 B 클래스로 옮기고 적절히 수정한다

1
2
3
4
5
6
7
8
9
class B {
public void moveMethod(int someVar) {
if(b.someCondition()) {
// do something
someVar;
// do something
}
}
}

여기서 적절한 수정이란, moveMethod를 B 클래스로 옮겼을 떄 여전히 필요한 A 클래스의 기능에 대한 것이다
여기선 A 클래스의 인스턴스 변수로 있었던 someVar가 된다

이럴경우 원본 클래스(A 클래스)의 기능을 사용하려면 아래 4가지 중 하나를 실시하면 된다

  • 그 기능을 대상 클래스로 옮긴다
  • 대상 클래스에서 원본 클래스로의 참조를 생성하여 사용한다
  • 원본 객체를 대상 객체 메서드의 매개변수로 전달한다
  • 그 기능이 변수라면 변수를 매개변수로 전달한다

지금은 그 기능이 변수였기 때문에 4번째 방법을 사용했다
만약 A 클래스의 다른 메서드를 사용해야하거나 참조하는 인스턴스 변수가 2개 이상이라면 클래스 자체를 매개변수로 전달해야한다

이제 A 클래스에서 원본 메서드를 삭제하거나 위임하는 방식으로 변경한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class A {
private int someVar;
private B b;

public void moveMethod() {
b.moveMethod(someVar);
}

public void someMethod() {
// do something
moveMethod();
// do something
}
}

// 또는

class A {
private int someVar;
private B b;

public void someMethod() {
// do something
b.moveMethod(someVar);
// do something
}
}

참고 : 마틴 파울러, 『리팩토링』, 김지원 옮김, 한빛미디어(2012)