[Refactoring] 냄새 17. 메시지 체인
08 Jan 2023해당 포스트는 inflearn의 백기선님의 강의인 리팩토링 을 듣고 정리한 글입니다.
냄새 17. 메시지 체인
- 레퍼런스를 따라 계속해서 메소드 호출이 이어지는 코드
- 예) this.member.getCredit().getLevel().getDescription()
- 해당 코드의 클라이언트가 코드 체인을 모두 이해해야 한다.
- 체인 중 일부가 변경된가면 클라이언트의 코드도 변경해야 한다.
- 관련 리팩토링
- “위임 숨기기 (Hide Delegate)”를 사용해 메시지 체인을 캡슐화를 할 수 있다. 캡슐화를 통해 클라이언트가 최소한의 정보만 알 수 있도록 변경할 수 있다.
- “함수 추출하기 (Extract Function)”로 메시지 체인 일부를 함수로 추출한 뒤 “함수 옮기기 (Move Function)”으로 해당 함수를 적절하게 이동할 수 있다.
리팩토링 37. 위임 숨기기
- 캡슐화 (Encapsulation)란 어떤 모듈이 시스템의 다른 모듈을 최소한으로 알아야 한다는 것이다. 그래야 어떤 모듈을 변경할 때, 최사한의 모듈만 그 변경에 영향을 받을 것이고, 그래야 무언가를 변경하기 쉽다.
- 처음 개게 지향에서 캡슐화를 배울 때 필드를 메소드로 숨기는 것이라 배우지만, 메소드 호출도 숨길 수 있다.
- person.department().manager(); -> person.getManager()
- 이전의 코드는 Department 를 통해 Manager에 접근할 수 있다는 정보를 알아야 하지만, getManager()를 통해 위임을 숨긴다면 클라이언트는 person의 getManager()만 알아도 된다. 나중에 getManager() 내부 구현이 변경될지라도 getManager()를 사용한 코드는 그대로 유지할 수 있다.
- Department
public class Department {
private String chargeCode;
private Person manager;
public Department(String chargeCode, Person manager) {
this.chargeCode = chargeCode;
this.manager = manager;
}
public String getChargeCode() {
return chargeCode;
}
public Person getManager() {
return manager;
}
}
- Person
public class Person {
private String name;
private Department department;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
여기서 해당 체이닝을 사용하는곳은 테스트코드에서 확인할 수 있다.
테스트 코드를 살펴보면 keesun.getDepartment().getManager();
으로 Manager 정보를 가져오는 것을 볼 수 있다.
person -> department -> manager 의 순으로 데이터를 가져온다.
@Test
void manager() {
Person keesun = new Person("keesun");
Person nick = new Person("nick");
keesun.setDepartment(new Department("m365deploy", nick));
Person manager = keesun.getDepartment().getManager();
assertEquals(nick, manager);
}
테스트는 성공했다.
이제 캡슐화를 통해 해당 체이닝을 변경해보도록 하자.
Person
클래스에 getManager()
메소드를 추가하여 manager 정보를 반환하도록 하였다.
public class Person {
...
Person getManager() {
return getDepartment().getManager();
}
}
해당 캡슐화를 진행한 이유는
만약 Manager의 정보를 Department를 더이상 거치지 않고 바로 가져오게 된다면, getManager()
의 코드는 아래와 같이 변경될 것이다.
public class Person {
private Person Manager;
...
Person getManager() {
return this.Manager;
}
}
하지만 호출부인 테스트 코드의 getManager()은 변경하지않아도 정상작동을 확인할 수 있다.
@Test
void getManager_refactor() {
Person nick = new Person("nick", null);
Person keesun = new Person("keesun", new Department(nick));
assertEquals(nick, keesun.getManager());
}