Tell, Don't Ask
아래의 글은 마틴 파울러의 Tell, Don't Ask를 번역한 글입니다.
서문
Tell, Don't Ask 원칙은 객체 지향이란, 하나의 데이터와 그 데이터를 이용해 동작하는 함수를 한 곳에 모아놓는 것 이라는 사실을 강조합니다. 이 원칙은 우리가 객체에게 데이터에 대한 정보를 요청(asking)하고 그 이후에 데이터에 대한 동작(acting)을 하는 대신에, 우리는 그저 객체에게 무엇을 해야하는지 요구(tell)해야 한다고 말하고 있습니다. 때문에 이 원칙은 행동(behavior)이 객체 외부에 존재하는 것이 아니라, 데이터와 함께 객체 내부에 두는 것을 권장합니다.
예시
명확하게 하기 위해 예시를 하나 들어보겠습니다. 우리는 특정 값을 모니터링하여 그 값이 특정 한도를 초과하면 알람(경고)을 보내야하는 코드를 작성한다고 가정해보겠습니다. 만약 이를 "요청(ask)" 스타일로 작성한다고 가정하면, 아래와 같이 데이터 구조를 만들 수 있을 것입니다.
class AskMonitor {
private int value;
private int limit;
private boolean isTooHigh;
private String name;
private Alarm alarm;
public AskMonitor (String name, int limit, Alarm alarm) {
this.name = name;
this.limit = limit;
this.alarm = alarm;
}
}
그리고 데이터에 접근하기 위해 접근자들을 만들어야 할 것입니다.
class AskMonitor...
public int getValue() {return value;}
public void setValue(int arg) {value = arg;}
public int getLimit() {return limit;}
public String getName() {return name;}
public Alarm getAlarm() {return alarm;}
그리고 이를 사용하는 코드는 아래와 같이 작성할 수 있을 것입니다.
AskMonitor am = new AskMonitor("Time Vortex Hocus", 2, alarm);
am.setValue(3);
if (am.getValue() > am.getLimit())
am.getAlarm().warn(am.getName() + " too high");
Tell, Don't Ask 원칙은 if (am.getValue() > am.getLimit())
같은 행동(behavior)을 외부에 두는 것이 아니라, 객체 내부에 두는 것을 권장합니다. 그래서 아래와 같이 작성할 수 있습니다.
class TellMonitor...
public void setValue(int arg) {
value = arg;
if (value > limit) alarm.warn(name + " too high");
}
이는 아래와 같이 사용할 수 있습니다.
TellMonitor tm = new TellMonitor("Time Vortex Hocus", 2, alarm);
tm.setValue(3);
마틴 파울러가 말하는 Tell, Don't Ask
많은 사람들은 "Tell, Don't-Ask" 원칙이 유용한 원칙이라고 생각합니다. 객체 지향 설계의 기본 원리 중 하나는 데이터와 행동(behavior)을 함께 묶음으로써, 시스템의 기본 요소인 이 둘을 결합하는 것입니다. 데이터와 이를 조작하는 동작은 서로 밀접하게 연결되어 있기 때문에 하나의 변화가 다른 변화를 일으키고, 하나를 이해하면 다른 하나를 이해하는 데 도움이 되기 때문에 이는 종종 좋은 일입니다. 긴밀하게 연결된 항목은 동일한 구성 요소에 있어야 합니다. "Tell, Don't Ask" 를 생각하면 프로그래머가 이러한 연계성을 높일 수 있는 방법을 파악하는 데 도움이 됩니다.
하지만 개인적으로 저는 "Tell, Don't Ask"을 사용하지 않습니다. 그저 저는 데이터와 행동(behavior)을 함께 묶는다는 관점에서 볼 뿐이며, 이와 같은 관점으로도 같은 결과를 얻어낼 수 있습니다. "Tell, Don't Ask"에 대해 문제가 되는 한 가지는 사람들이 모든 쿼리 메서드를 제거하려는 GetterEradicator(Getter 박멸자)가 되는 것을 보았다는 것입니다. Getter를 통해 객체가 정보를 외부에 제공함으로써 객체들이 효과적으로 협력하는 경우도 충분히 있습니다. 좋은 예로, 입력 정보를 받아 클라이언트를 단순화하기 위해 변형하는 객체(예시: EmbeddedDocument 사용)가 있습니다. 저는 개발자들이 "Tell, Dont' Ask" 원칙을 지나치게 엄격하게 따르려고 할 때 코드가 불필요하게 복잡해지는 경우를 많이 봤습니다. 사실, 적절하게 정보를 제공하는 메소드를 사용하는 것이 훨씬 더 간단하고 명확한 해결책이 될 수 있습니다(그리고 실제로 데이터와 동작을 함께 배치하는 보다 근본적인 원칙조차도 레이어링과 같은 다른 문제를 위해 포기해야 할 때도 있습니다. 좋은 디자인은 장단점을 고려해야 하며, 데이터와 동작을 함께 배치하는 것은 염두에 두어야 할 요소 중 하나일 뿐입니다.). 저에게 있어 "Tell, Don't Ask"는 데이터와 행동(behavior)을 함께 배치하기 위한 발판일 뿐이지, 강조할 만한 요점은 아니라고 생각합니다.