[Tell, Don’t Ask] 물어보지 말고 시켜라

http://c2.com/cgi/wiki?TellDontAsk

주요 내용
– Shy Code를 작성하라.
– 어떤 객체로부터 정보를 가져와서 판단하지 말고 그 객체가 판단하도록 시키자.
– Command와 Query를 구분하자.
– Law of Demeter를 지키자.
메서드가 어디에 어느 클래스에 위치 해야 하는지에 대한 이야기인데, 가끔 코딩하다보면 이 코드가 여기 있어야 되는건지..저기로 가야하는건지 판단의 기준이 잘 서지 않을 때가 있는데 그럴 때 이 말을 떠올리면 도움이 될 것 같습니다.
 public boolean confimMember(String email, String authCode) {
Member storedMember = repository.findByEmail(email);
if (storedMember == null) {
throw new UsernameNotFoundException(email + ” 에 해당하는 사용자가 없습니다.”);
}
boolean result = storedMember.getAuthCode().equals(authCode);
if (!result) {
throw new InsufficientAuthenticationException(authCode
+ ” 인증 코드가 올바르지 않습니다.”);
} else {
storedMember.join();
storedMember.makeAvatar();
addMemberRole(storedMember);
update(storedMember);
}
return result;
}
이 코드는 봄싹의 MemberServiceImpl 코드중 일부입니다. 즉 위와 같은 코드는 저 원칙에 위배되는 코드로 객체 지향적이지 못한 코드입니다. 객체 지향적인 코드라면 함수 호출이 아니라 어떤 작업을(method) 지시해야 합니다. 그런데 지금은 authCode를 가져와서 매개변수의 authCode와 같은지 판다하고 있지요. 바로 이 예제가 Tell, Don’t Ask에서 지적하고 있는 대표적인 사례입니다.
boolean result = storedMember.isConfirmed(authCode);
이렇게 매개변수로 필요한 정보를 전달하고 결과를 돌려받으면 됩니다. 즉 isConfirmed()라는 Query 메서드를 사용해서 정보를 조회하면 됩니다. Command 메서드가 아니라 Query 메서드기 때문에 당연히 객체 내부 정보를 변경하지 않도록 주의해야 합니다. Command 메서드와 Query 메서드를 잘 구분하는 것이 저 원칙을 지키는 지름길입니다. 또한, 이렇게 하는 것이 ‘데메테르 법칙’을 지키는 일이기도 합니다. 최측근에게만 일을 시키게 되니까 말이죠.
단, 단점이 있는데 그건 바로. 저 상태로 끝나면 모를까. 그게 아니라 Member 클래스에 isConfirm() 메서드를 만들어 줘야 한다는 거죠. 만약 3중, 4중으로 건너 건너 요청하던 코드를 저 원칙에 따라 고친다면 그만큼 자잘한 코드들이 생길 겁니다. 
그런 단점들 대신에 얻을 수 있는 장점으로 시스템 내부의 클래스간 의존도를 낮출 수 있다는 것입니다. 따라서 그 둘간의 저울질을 잘 하고 선택하시기를…
봄싹 코드는 이제 저 기준에 따라 모든 클래스의 코드를 리뷰하고 코드 뜯어고치기 작업을 진행합니다. 그게 끝나면 패키지 간의 의존성을 점검해봐야겠습니다.

8 thoughts on “[Tell, Don’t Ask] 물어보지 말고 시켜라”

  1. 1. ask는 물어보지 말라는 얘기가 아니라 데이터를 요청하지 말라는 것.
    2. result가 false면 예외인데 리턴은 또 머하러 하나. 결과를 전달하는 방법을 하나로 결정할 것.
    3. confirm 작업이 목적이라면 아예 storedMember에 confirm() 메소드를 만들고,
    confirm시 하는 storedMember의 내부 작업(아래 두 줄)은 그 안에서 하도록 하는게 더 나을 듯
    storedMember.join();
    storedMember.makeAvatar();
    InsufficientAuthenticationException 예외도 거기서 발생시키고.

    4. 전반적으로 메소드 작업의 추상화 레벨이 산만하고, 뭐하는 용도인지도 불분명 한 듯.

    1. 1. 데이터 요청 -> 물어보다. 로 매핑하기엔 좀 그런가요 흠.. 전 어울리다고 생각했는데.. @_@;

      2. 헐.. 그렇네요;; false를 리턴하던지 Member에서 예외를 던지던지 할껄;;

      3. 오호. 그게 좋겠습니다.

      4. 넵.

    2. 그럼 원제에 Tell, Don’t Ask (data for making decisions) 라고 써줬으면 좀 더 명확했을텐데; Query 메서드 이야기를 봐서는 isConfirmed() 같이 물어보는건 괜찮지 않을가요?

      글 내용들이 아주 물어보지 말라는것도 아니면서 원제를 일부러 (짧게 쓰려고) 저렇게 지은 듯.

      저는 단지 원제에 충실하게 번역했을 뿐이라는.. @_@

  2. 아래 부분은 repository내 데이터 로직으로 만들어도 될 듯. findRequestedMember… 어쩌고.

    if (storedMember == null) {
    throw new UsernameNotFoundException(email + ” 에 해당하는 사용자가 없습니다.”);
    }

Leave a Reply

Your email address will not be published. Required fields are marked *