괴발나라

[GOF] 커맨드 패턴으로 우콩 만들기 🐒 본문

도서/JAVA 객체지향 디자인패턴

[GOF] 커맨드 패턴으로 우콩 만들기 🐒

괴발맨 2022. 2. 6. 17:15

오늘은 커맨드 패턴에 대해 알아보겠습니다.

커맨드 패턴은 이벤트를 발생시키는 객체(Invoker)와

이벤트를 받는 객체(Reciever)로 구성되어 있습니다.

 

가령 어떤 버튼을 클릭했을때 전등이 켜지거나 꺼진다면,

그 버튼은 "클릭됨" 이라는 이벤트를 발생시키고

전등은 그 이벤트를 수신하여 켜지거나 꺼지겠죠?

 

여기서 버튼은 Invoker, 전등은 Reciever가 됩니다.

커맨드 패턴은 이런 기능을 SOLID원칙을 지키며 구현할 수 있도록 해줍니다.


오공으로 미니언 처치하기

LOL(리그오브레전드)이라는 게임을 아시나요?

LOL에서는 게이머가 한 챔피언(캐릭터)을 골라

미니언(적)을 처치하며 챔피언의 레벨을 올릴 수 있습니다. 

 

챔피언이 Invoker이고, 미니언은 Reciever라고 해봅시다.

챔피언이 "공격"이라는 이벤트를 발생시키면,

미니언의 체력은 줄어듭니다.

 

이제 코드로 구현해볼 것입니다.

챔피언중에는 "오공"이라는 캐릭터가 있는데요, 

손오공이라고 생각하시면 됩니다.

 

그럼 한번 이 오공 챔피언을 클래스로 만들어보겠습니다.

interface Champion {
    void attack(Minion minion);
}

class Wukong implements Champion {

    @Override
    public void attack(Minion minion) {
        System.out.println("기본 공격!");
        minion.decreaseHp(10); // 미니언 체력 5 감소
    }
}

Champion 인터페이스를 만든 이유는 챔피언은 오공만이 아닌 여러개가 있기 때문입니다.

 

그러면 미니언도 클래스로 만들어 봅시다.

class Minion {
    private int hp;

    Minion(int hp){
        this.hp = hp;
    }

    void decreaseHp(int amount) {
        this.hp -= amount;
        System.out.println("미니언의 hp가 " + this.hp + "가 되었습니다.");
    }
}

공격 시 미니언의 체력(hp)를 줄여야 하기 때문에 decreaseHp라는 메서드를 만들었습니다.

 

현재 오공은 기본 공격만 할 수 있습니다.

더 데미지가 높은 "근두운 급습(W)" 스킬로 공격하게 하려면 다음과 같이 변경해야합니다.

class Wukong implements Champion {

    @Override
    public void attack(Minion minion) {
        //System.out.println("기본 공격!");
        //minion.decreaseHp(5);
        System.out.println("근두운 급습!");
        minion.decreaseHp(20); // 미니언 체력 20 감소
    }
}

"파쇄격(Q)"로 공격하려면 또 위와 같은 방식으로 변경해줘야 합니다.

공격 기술을 변경하려면 코드를 주석처리하고 새로 작성하는 작업을 반복해야 합니다.

심지어 실행 시 동적으로 공격 기술을 바꿀 수 도 없습니다.

 

커맨드 패턴을 이용해서 게이머가 원하는 공격 기술을 자유롭게 선택할 수 있도록 해줍시다.


커맨드 패턴으로 개선시키기 

먼저 공격 커맨드 인터페이스를 만들어 공격 기능을 추상화시킵니다.

interface AttackCommand {
    void execute();
}

// 기본 공격 커맨드
class BasicAttackCommand implements AttackCommand {
    private Minion minion;

    BasicAttackCommand(Minion minion) {
        this.minion = minion;
    }


    @Override
    public void execute() {
        System.out.println("기본 공격!");
        this.minion.decreaseHp(5);
    }
}
// 파쇄격 공격 커맨드
class QAttackCommand implements AttackCommand {
    private Minion minion;

    QAttackCommand(Minion minion) {
        this.minion = minion;
    }

    @Override
    public void execute() {
        System.out.println("파쇄격!");
        this.minion.decreaseHp(10);
    }
}
// 근두운 급습 공격 커맨드
class WAttackCommand implements AttackCommand {
    private Minion minion;

    WAttackCommand(Minion minion) {
        this.minion = minion;
    }

    @Override
    public void execute() {
        System.out.println("근두운 급습!");
        this.minion.decreaseHp(20);
    }
}

조금 복잡해졌지만, 

충분히 공격 기능을 인터페이스로 추상화할 가치가 있습니다.

오공 클래스의 코드를 보면 아실겁니다.

class Wukong implements Champion {
    private AttackCommand attackCommand; 

    public void setAttackCommand(AttackCommand attackCommand) {
        this.attackCommand = attackCommand; // 공격 커맨드를 외부에서 선택합니다.
    }

    @Override
    public void attack() {
        this.attackCommand.execute(); // 공격 커맨드에게 기능을 위임만 하고 끝납니다.
    }
}

 

게이머가 공격 기능을 자유롭게 변경할 수 있게 되었습니다.

직접 공격 기능을 변경해가며 미니언을 처치해봅시다!

public class Client {
    public static void main(String[] args) {

        Minion minion = new Minion(100); // 미니언의 체력은 100입니다.

        AttackCommand click = new BasicAttackCommand(minion); // 기본 공격
        AttackCommand Q = new QAttackCommand(minion); // 파쇄격
        AttackCommand W = new WAttackCommand(minion); // 근두운 급습

        Wukong wukong = new Wukong();

        wukong.setAttackCommand(click);
        wukong.attack();
        // 기본 공격!
        // 미니언의 hp가 95가 되었습니다.

        wukong.setAttackCommand(Q);
        wukong.attack();
        // 파쇄격!
        // 미니언의 hp가 85가 되었습니다.

        wukong.setAttackCommand(W);
        wukong.attack();
        // 근두운 급습!
        // 미니언의 hp가 65가 되었습니다.
    }
}

 

다른 챔피언을 만들어 새로운 공격 커맨드를 작성해도 

매우 재밌는 작업이 될 듯 합니다.

감사합니다.