2.2 장면 2: 원칙은 깨어져선 안 된다.
[며칠 후, 돈은 까다로운 설계 문제로 인해 제인에게 도움을 요청한다.]
돈:
|
제인, 시간 있어?
|
제인:
|
물론. 무슨 일이야?
|
돈:
|
또 다른 설계 문제가 생겨서 말이야, 너한테 도움을 받을 수 있을까 해서.
|
제인:
|
물론이지.
|
돈:
|
심사 과정이 어떻게 작동할지에 대해 몇 가지 측면을 알아보려고 해. Claim이 어떻게 수락되어 지급되는지와 어떻게 거부되는지에 대해서 말이야.
|
제인:
|
응, 지난번에 네가 설명해줬던 기억이 나.
|
돈:
|
내 문제는 바로 이거야. 보상금을 청구하는 방법으로 이렇게 내려오거든. 요구문서에서 설명하는 원칙들이 서로 달라서 말이야. 어떻게 나타낼지 모르겠어.
|
제인:
|
요구문서 좀 보여줄래? [돈이 문서를 건네주고 다음 단락을 함께 읽는다]
- 정책은 보장되는 항목의 목록으로 구성되어 있고, 각 항목과 관련된 지급원칙도 함께 있 다. 지급에는 몇 가지 원칙이 있다:
- 특정 항목에서는 절차와 관련해 전혀 보상하지 않는다 (예: 보상 거부).
- 특정 항목에서는 균일한 달러 금액을 지급한다 (예: 123.4 절차의 경우 $25를 지불한다).
- 특정 항목에서는 해당하는 비용 중 일정 비율만 지급한다 (예: 234.5 절차의 경우 병원비의 50%를 지급한다).
그리고 지급원칙이 두 가지가 더 있어.
- 손절매 원칙(Stop-loss rule)은 개별 항목에 지급되는 최대금액을 조절한다 (예: 비용의 70% 또는 $500 중 적은 금액을 지급한다).
- 쿼리기반의 원칙(Query-based rule)은 청구인의 속성을 기반으로 한다 (예: 청구인이 여성일 경우 본 절차에 $200를 지급하고; 그 외에는 $150를 지급한다).
|
돈:
|
어떻게든 이런 절차 코드를 서로 다른 원칙에 맞출 수는 있었어. 하지만 각 원칙은 사용자가 결정한단 말이지! 원칙을 Policy로 코드화할 수 있으면 좋겠지만, 문제는 사용자가 어떤 원칙을 원할 것인지 미리 아는 방법이 없다는 거야.
|
제인:
|
내가 보기엔 각각의 "원칙"들이 보상 전략(Strategy)으로 들리는데?
|
돈:
|
그렇게 말하니 흥미로운데? 무슨 의미지?
|
제인:
|
Strategy는 또 다른 패턴이야. Strategy 패턴의 의도를 읽어줄게 [디자인 패턴 서적에 다가가 책을 펼친 후 다음 문장을 읽는다] : "알고리즘 군을 정의하고, 각각의 알고리즘을 별도의 클래스로 캡슐화하여 상호 교환이 가능하게 만든다. Strategy 패턴은 이를 사용하는 클라이언트에게 영향을 주지 않고 독립적으로 다양하게 나타난다."
|
돈:
|
글쎄, 보상은 알고리즘이 아닌 것 같은데. 그래도 시도는 해보고 싶어. Strategy 패턴이 어떻게 실행되는지 보여줘 봐.
|
제인:
|
각각의 원칙을 하나의 Strategy 객체로 만들 순 없는지 살펴보자. 기본적으로 각 원칙은 한 라인 항목에 대한 총합을 계산하는 것이 맞지?
|
돈:
|
맞아.
|
제인:
|
그러니까 각각의 객체는 reimbursementFor: aLineItem라고 불리는 메시지를 이해해야만 해. 각 원칙마다 하나의 클래스가 있어. 네가 설명한 첫 번째 원칙이 뭐였지?
|
돈:
|
"절차와 관련해 전혀 보상하지 않는다."
|
제인:
|
특별한 사례의 경우네. 이 원칙을 reimbursementFor: 로 구현하는 건 쉽겠어: 반환(return) 값이 항상 0이 되니까.
|
돈:
|
두 번째는 어때? 균일한 달러 금액을 지급하는 원칙 말이야.
|
제인:
|
보상금액을 계속 유지하는 인스턴스 변수가 하나 있을 거야. 그럼 reimbursementFor: 메소드가 그 인스턴스 변수 값을 반환할거야.
|
돈:
|
이제 이해가 가기 시작했어. 그럼 "라인 항목에 해당하는 비용 중 일정 비율만 지급"하는 원칙은 퍼센트를 저장하고 reimbursementFor: 메소드가 라인 항목의 비용에 퍼센트를 곱한 결과를 반환하겠지?
|
제인:
|
맞아. 3개의 클래스 계층구조는 이렇게 되겠지.
|
제인:
|
Claim은 특정 절차 코드에 어떤 원칙을 적용할 것인지 찾아본 후에 그 절차에 맞는 원칙을 적용할 필요가 있어.
|
돈:
|
나쁘지 않은 걸! 문제 일부는 해결됐네. 그럼 다음 건 어때? 손절매 원칙은 앞서 말한 두 원칙처럼 작동하질 않아. 다른 원칙 하나를 먼저 실행한 후에 그 결과를 바탕으로 상환금액을 결정해야 할 것 같은데.
|
제인:
|
네가 뭔가 중요한 걸 발견한 것 같은데. 한번 찾아볼게 [디자인 패턴 서적을 들고 책을 넘기기 시작한다]. 여기 새로운 행위를 추가하기 위해 런타임에서 객체의 행위를 변경하는 방법을 알려주는 패턴이 있어. 아, 찾았다! Decorator 패턴이야 [책을 넘겨 177페이지에 실린 다이어그램을 돈에게 보여준다]. 이 다이어그램에서 어떻게 Decorator가 기존 객체가 아닌 다른 객체의 인스턴스를 포함시키면서 기존 객체와 동일한 인터페이스를 구현하는지 이해하겠어? 네 문제도 비슷하게 해결하면 될 것 같아.
|
돈:
|
또 헷갈리네. 기존 객체는 뭐고 다른 객체는 뭐야?
|
제인:
|
구체적으로 말해줄게. StopLossStrategy라 불리는 다른 원칙이 있다고 가정하고, 그 안에 다른 Strategy가 포함되어 있다고 쳐. 그럼 이 원칙은 원칙에 포함된 Strategy 로 메시지를 전송(forwarding)한 다음 그 결과가 손절매 금액을 초과하는지 확인함으로써 reimbursementFor: 메소드를 구현하는 거야.
|
돈:
|
'[망설이며] 네가 무슨 말을 하는지 알 것 같아. 이런 모양이 되겠다는 말이지? [제인의 그림을 가져와 새로운 클래스를 추가한다].
|
제인:
|
맞아.
|
돈:
|
그럼 이 디자인 패턴이란 건 정말 효과적인걸! 하나의 설계에 하나 이상의 패턴을 통합했잖아. 앞에서 네가 설명한 방법대로라면 독자적으로 작동할 줄 알았거든.
|
제인:
|
항상 그런건 아냐. 디자인 패턴은 하나만 사용할 수 있지만 대개는 하나의 설계에 몇 가지 패턴이 함께 사용되는 걸 볼 수 있을 거야. 함께 자주 사용되는 특정한 패턴들이 있어. [디자인 패턴]의 각 패턴 마지막 부분에 "관련 패턴"이라는 절에서 열거하고 있어.
|
돈:
|
저자들이 그걸 생각해 냈다니 다행이야. 하지만 어떤 패턴을 적용할 것인지는 어떻게 알지? 넌 그냥 임의로 뽑은 것 같아서 말이야.
|
제인:
|
꼭 그렇지만도 않아. 내가 패턴을 선택하는 과정이 전혀 과학적이지 않다는 건 인정하지만 말이야. 지난 번 PLoP(Patterns Languages of Programs) 학회에서 누군가 한 패턴 전문가에게 어떻게 패턴을 결정했는지 질문하는 걸 우연히 들었거든. 전문가는 "내가 읽었지만 절반은 잊어버린 종이 조각에 대해 생각해보고, [디자인 패턴] 내부의 맨 앞 양면 페이지를 살피기도 하고, 때로는 추측에 맡겼죠,"라는 말을 했어. 패턴을 읽고 제2의 천성이 될 때까지 충분히 적용해보는 것이 핵심인 것 같아.
|
돈:
|
그렇구나. 패턴에 관해 실제로 읽을 필요가 있는 것 같아.
|
제인:
|
정말 많이 배울 수 있을 거야. 내 책을 빌려갈래? [책을 돈에게 건네준다].
|
돈:
|
당연하지. [그는 책을 받는다]. 가서 이 설계를 문서화 시켜야겠어. 그건 그렇고 쿼리기반의 원칙에 대해서는 해줄 말 있어?
|
제인:
|
글쎄, 런타임 쿼리를 처리하는 Interpreter 패턴을 이용할 수 있지. [디자인 패턴] 243페이지를 찾아 봐.
|
돈:
|
좋았어! 한 번 읽어볼게. [제인의 칸막이 사무실을 급히 뛰어나가며 책을 흔든다]
|