딤 이블(Deam Evil)은 기사의 결투 시합 게임을 한다. 상금은 10만 달러이다.
“만약 당신들이 시스템을 깨고 나의 중무장한 코만도를 이 시합에 넣어 주면 상금의 반을 주겠소.”
페드로: 드디어 재밌는 일을 하게 되네요.
이브: 시합을 보는 것은 재밌죠. 특히 M16과 철검의 대결이라면…
페드로: 기사들은 좋은 갑옷을 입고 있어요.
이브: F1 수류탄한테는 갑옷은 소용 없어요.
페드로: 신경쓸 거 없어요, 우리는 일을 하고, 돈을 받으면 돼요.
이브: 5만 달러 - 괜찮은 액수네요.
페드로: 네, 이것을 보세요, 시합 시스템의 소스를 훔쳤어요. 소스를 고칠 수는 없지만, 약점을 찾을 수 있어요.
이브: 여기 있어요.
public interface Tournament { void accept(Knight knight); }
페드로: 아하! 시스템은 Knight 인터페이스로 들어오는 입력 타입만을 검사하고 있어요. 우리는 그저 commando를 기사로 맞추어 넣기(adapt)만 하면 돼요. 기사가 어떤 모양인지 봅시다.
interface Knight { void attackWithSword(); void attackWithBow(); void blockWithShield(); } class Galahad implements Knight { @Override public void blockWithShield() { winkToQueen(); take(shield); block(); } @Override public void attackWithBow() { winkToQueen(); take(bow); attack(); } @Override public void attackWithSword() { winkToQueen(); take(sword); attack(); } }
페드로: 코만도를 받기 위해서 이전 구현 코드를 가져옵시다.
class Commando { void throwGrenade(String grenade) { } shot(String rifleType) { } }
페드로: 그리고 코만도를 기사로 맞추어 줍니다.
class Commando implements Knight { @Override public void blockWithShield() { // commando don't block } @Override public void attackWithBow() { throwGrenade("F1"); } @Override public void attackWithSword() { shotWithRifle("M16"); } }
페드로: 됐어요.
이브: 클로저로는 더 쉬워요.
페드로: 정말로요?
이브: 클로저는 타입을 쓰지 않아서 타입 검사는 전혀 동작하지 않죠.
페드로: 그러면 기사를 어떻게 코만도로 바꾸죠?
이브: 기본적으로 기사란 무엇이죠? 그것은 맵이죠, 데이타와 행위로 된.
{:name "Lancelot" :speed 1.0 :attack-bow-fn attack-with-bow :attack-sword-fn attack-with-sword :block-fn block-with-shield}
이브: 코만도를 기사로 맞추기 위해서는 단지 원래의 것 대신 코만도의 함수를 전달하면 되요.
{:name "Commando" :speed 5.0 :attack-bow-fn (partial throw-grenade "F1") :attack-sword-fn (partial shot "M16") :block-fn nil}
페드로: 우리는 돈을 어떻게 나눌까요?
이브: 50 대 50
페드로: 내가 더 많은 코드를 작성했으니, 전 70을 원해요.
이브: 오케이, 70 대 70
페드로: 좋아요.