리팩토링 38. 중재자 제거하기

    들어가기 전

    이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다. 


    리팩토링 38. 중재자 제거하기 (Remove Middle Man)

    • '중재자' 냄새는 다음과 같다.
      • 캡슐화가 과도한 경우, 너무 많은 메서드가 단순 위임 메서드가 될 수 있다. 
      • 왜 클래스를 바로 호출하지 않고 위임해서 호출하는거지? 라는 생각이 들 때, 코드는 '중재자 냄새'를 풍긴다. 
      • '위임 숨기기'의 반대에 해당하는 리팩토링임. 
    • 필요한 캡슐화의 정도는 시간 / 상황 / 사람에 따라 바뀔 수 있다. 따라서 중재자 / 위임 숨기기의 리팩토링을 적절히 균형을 맞추는 것이 좋다.
    • 캡슐화의 정도를 '중재자 제거하기'와 '위임 숨기기' 리팩토링을 통해 조절할 수 있다.
    • 리팩토링 방법
      • 위임하고 있는 객체를 클라이언트가 사용할 수 있도록 getter를 제공하고, 클라이언트는 메세지 체인을 사용하도록 코드를 고친 뒤에 캡슐화에 사용했던 메서드를 제거한다.

     

    '중재자 제거하기' 리팩토링은 오히려 '메서드 체인'을 사용하도록 권장하는 리팩토링이다. 필요로 하는 캡슐화의 정도는 시간 / 상황에 따라 달라지는데, 그 때는 '메서드 체인'을 숨겨야 하는게 맞았을지도 모르지만 지금은 '메서드 체인'을 숨기지 않는 것이 맞는 경우도 존재한다. 예를 들어 '왜 중간 객체를 통해서 항상 참조해야하지? 저 객체를 직접 사용하는게 더 편할 거 같은데?'라는 생각이 들 때가 있다. 이 때는 중재자 냄새로 판단할 수 있고, 캡슐화를 통해 숨겼던 위임에 바로 접근하는게 좋을 수도 있다. 

    중재자 제거하기 / 위임 숨기기 리팩토링은 사실 보는 관점마다 달라질 수 있고, 크리티컬한 리팩토링이 아니기 때문에 이걸로 많은 시간을 보낼 필요는 없다. 그 때마다 적절한 수준의 캡슐화를 대략적으로 선택해서 중재자 제거 / 위임 숨기기 등으로 잘 조절하면 된다. 


    코드 살펴보기

    Person 클래스를 살펴보자. Person의 getManager() 메서드는 메서드 체이닝을 위임 숨기기를 통해서 getManager()를 가져온다. 쉽게 말해서 Person 클래스는 department 필드에게 요청해서 Manager를 전달받아 그것을 단순히 반환하는 용도로 만들어진다

    그런데 예전에는 이 정도의 캡슐화가 좋다고 생각했는데, 지금은 과한 캡슐화라고 생각할 수 있다. 따라서 이런 위임을 '중재자 냄새'로 판단하고, 위임을 통해서가 아니라 앞으로 직접 호출하는 것으로 바꾸기로 결정했다고 가정해보자. 그러면 '중재자'를 제거하면 된다. 

    public class Person {
    
        private Department department;
        private String name;
    
        public Person(String name, Department department) {
            this.name = name;
            this.department = department;
        }
    
        public Person getManager() {
            // 그냥 department를 통해서 직접 manager를 호출하는게 더 좋을 듯? (중재자 냄새)
            return this.department.getManager();
        }
    }

    리팩토링은 다음과 같이 접근한다.

    1. department 필드에 대한 getter()를 제공한다.
    2. getManager() 메서드를 Person 클래스에서 삭제한다.
    3. 클라이언트에서는 person.getDepartment().getManager()를 통해서 메서드 체이닝을 이용한다. 

    리팩토링 결과는 다음과 같다. 

    public class Person {
    
        private Department department;
        private String name;
    
        public Person(String name, Department department) {
            this.name = name;
            this.department = department;
        }
    
        public Department getDepartment() {
            // Department를 반환해주고, 이를 통해 manager를 직접 호출.
            // getManager() 메서드는 삭제했다 → 중재자 제거
            return this.department;
        }
    }
    
    public class Department {
    
        private Person manager;
    
        public Department(Person manager) {
            this.manager = manager;
        }
    
        public Person getManager() {
            return manager;
        }
    }

    댓글

    Designed by JB FACTORY