전략 패턴 (Stretegy Pattern)을 이용한 드래곤볼 게임 개발 #2

안녕하세요. 연두아빠에요.

전략 패턴 관련 두번째 포스팅입니다.


지난 포스팅에 이어서 이번에는 실제로 액션감 충만한 드래곤볼 게임 개발을 해보겠습니다.



지난 번에도 말씀 드렸지만, 이 정도로 퀄리티 있는 게임은 아니구요.


우리의 목표는 디자인 패턴을 공부하는 것이라는 것을 다시 한번 리마인드 하면서... 시작해 보겠습니다.



지난 포스팅에서 상속을 이용해서 간단하게 드래곤 볼 게임을 만들어 보려다 머리만 더 아프고


기획이 추가/변경 될 때마다 구조를 계속해서 변경해야 하는 고통에 시달리게 되었었는데요.


왜 계속해서 구조를 계속 바꿔줘야 했던건지... 가장 큰 문제가 뭐였을까요?

상속

네... 그저 상속으로만 다 해결을 해보려다 이 사단이 나게 된 것 같네요.


부르마 처럼 싸우지 못하는 케릭터도 있고, 찌찌처럼 하늘을 날 수 없는 케릭터도 있고,


미스터 사탄 처럼 하늘을 날아다니다가 로켓 부스터가 부서지면 더이상 못 날게 되는 케릭터도 있죠.


크리링과 오공은 둘 다 에네르기파라는 기술을 사용할 수 있지만, 오공은 원기옥도 사용이 가능하구요.


이렇게 저마다 케릭터들 고유의 특성이 다른데 하나의 부모 클래스에 공통적인 기능을 상속받아서


오버라이드를 하게 되면 중복 코드도 넘처나고 코드 재사용도 쉽지 않게 됩니다.


기획이 변경되고 케릭터가 추가될 때마다 클래스 구조를 이리 바꿨다 저리 바꿨다 하다보면,


결국 코드를 복사해서 붙여넣고 수작업으로 예외 처리를 하는 과정에서 새로운 버그들이 생겨날테구요.



이렇게 자주 변경 사항이 발생할 요지가 있는 부분들은 별도로 추출해서 캡슐화 하는 것이 좋습니다.


그렇게 되면 기획이 변경되고 새로운 케릭터가 추가 되어도 기존 코드 재활용이 수월해 지겠죠.


그렇다면, Character 클래스에서 변경 사항이 발생할 요지가 있는 부분이 어디일까요?


바로 Fly() 함수와, Attack() 함수 이 두 녀석이 되겠네요.


하늘을 날 수 있는 비행 동작과, 공격을 할 수 있는 동작들을 별도의 클래스로 추출해보겠습니다.



우리는 드래곤볼 게임을 C#으로 개발할 것이기 때문에 부모 클래스를 추상 클래스를 사용하지 않고,


인터페이스를 이용해서 구현했습니다.


C#에서 인터페이스와 추상 클래스의 가장 큰 차이점은 인터페이스는 상속 관계가 아니기 때문에


상속은 하나의 부모 클래스 만을 가질 수 있는 반면, 인터페이스는 무제한으로 참조할 수가 있습니다.


그러나 인터페이스는 그냥 골격만을 갖는 형태이기 때문에 실제 구현 코드는 넣을 수가 없기도 합니다.


디자인 패턴에서는 추상 클래스 보다는 인터페이스를 사용하는 것이 훨씬 장점이 많은데요.


이 주제에 대해서는 다음에 시간이 나면 중점적으로 다뤄보도록 하겠습니다.


어쨋든 여기까지의 내용을 코드로 옮겨보면 다음과 같습니다.


< IFlyable.cs >

public interface IFlyable

{

    string Fly();

}


< IAttackable.cs >

public interface IAttackable

{

    string Attack();

}


위와 같이 간단하게 인터페이스를 만들어 주고요.


IFlyable을 상속 받는 두 개의 클래스를 만들어 보면 다음과 같겠네요.


< FlyNoWay.cs >

public class FlyNoWay : IFlyable

{

    public string Fly()

    {

        return string.Empty;

    }

}


< FlyWithKi.cs >

public class FlyWithKi : IFlyable

{

    public string Fly()

    {

        return "기를 이용해 하늘을 날아서";

    }

}


어떤 식으로 게임을 만들게 될지 슬슬 감이 오시나요?


다음으로 IAttackable을 상속 받는 세 개의 클래스도 살펴 보겠습니다.


< AttackNoWay.cs >

public class AttackNoWay : IAttackable

{

    public string Attack()

    {

        return "무서워 공격할 수 없어";

    }

}


< AttackWithKi.cs >

public class AttackWithKi : IAttackable

{

    public string Attack()

    {

        return "에네르기파!";

    }

}


< AttackWithFist.cs >

public class AttackWithFist : IAttackable

{

    public string Attack()

    {

        return "분노의 주먹질!";

    }

}


이걸로 비행과 공격에 대한 행동을 다 처리한 것 같습니다.


이제 드디어 IFlyable과 IAttackable이라는 두 개의 인터페이스를 이용해서 비행과 공격 관련 행동을


Character 클래스 밖으로 추출해 낼 수 있게 되었네요.


그 다음에는 Character 클래스 내부에 IFlyable과 IAttackable을 변수로 추가하면 될 것 같습니다.



짠! 이런식으로요.


이렇게 구현을 하니 확실히 각각의 케릭터의 특성이 조금은 더 눈에 잘 들어오네요.


위의 케릭터 클래스 들을 코드로 옮겨보면 다음과 같겠네요.


< Character.cs >

public class Character

{

    protected IFlyable FlyBehaviour = null;

    protected IAttackable AttackBehaviour = null;


    public virtual string Action()

    {

        string action = string.Empty;

        if (null != FlyBehaviour)

        {

            action += FlyBehaviour.Fly();

        }


        if (null != AttackBehaviour)

        {

            action += AttackBehaviour.Attack();

        }


        return action;

    }

}


< Okong.cs >

public class Okong : Character

{

    public Okong()

    {

        FlyBehaviour = new FlyWithKi();

        AttackBehaviour = new AttackWithKi();

    }

}


< MrSatan.cs >

public class MrSatan : Character

{

    public MrSatan()

    {

        FlyBehaviour = new FlyNoWay();

        AttackBehaviour = new AttackWithFist();

    }

}


< Bulma.cs >

public class Bulma : Character

{

    public Bulma()

    {

        FlyBehaviour = new FlyNoWay();

        AttackBehaviour = new AttackNoWay();

    }

}


끝입니다! 이걸로 개발 완료 처리하면 될 것 같습니다.


실제 게임 로직 관련 코드는 매우 간단하고 주제와 벗어나는 부분이니 지면 관계상 생략을 하겠습니다.


궁금하신 분은 첨부된 전체 코드 파일을 참조 하시면 되겠고요. 어찌 됐든 드래곤볼 게임 완성!


실행하면 아래와 같이 케릭터를 선택할 때마다 죽여주는 슈퍼 액션이 텍스트로 출력됩니다.





진짜 흥미진진한 게임 아닌가요? 하하...


직접 플레이 해보고 싶으신 분들은 전체 코드를 첨부해 드리니 꼭 받아서 테스트 해보시면 됩니다.



오늘 배운 내용이 바로 전략 패턴입니다. 사실 뭐 별거 없었죠?


보통은 그냥 스트레티지 패턴(Stretegy Pattern)이라고 더 많이 부르긴 하죠.


구조자체가 심플해서 간단히 설명하기 쉽다보니 보통 디자인 패턴 서적에서 제일 먼저 다루곤 하죠.


저라고 뭐 예외가 있겠습니까? 단지 전 게임 개발에 포커스를 두고 드래곤볼 게임을 예로 들어 정리해봤습니다.



사실 오늘 배운 전략 패턴의 형태 말고도 더 복잡한 전략 패턴 역시 존재합니다.


그러나 그것은 전략 패턴의 활용이나 응용 쯤이 될 뿐이지 결국엔 기본은 오늘 배운 내용이 전부입니다.


전략 패턴은 갑작스런 알고리즘의 변화에도 유연하게 대처가 가능합니다.


지속적으로 변경이 일어나는 알고리즘들을 캡슐화해서 가지고 있기 때문이죠.



덕분에 오늘 만들어 본 드래곤볼 게임에 갑작스레 크리링 케릭터가 추가 되더라도 당황할 필요가 없죠.


오공과 같은 공격 패턴을 사용하니 해당 공격 알고리즘만 할당해주면 될테니까요.


미스터 사탄이 갑자기 로켓 부스터를 이용해 하늘을 날게 되더라도 알고리즘만 만들면 되겠네요.


미스터 사탄의 등 뒤의 로켓 부스터가 게임 중에 부서지면 하늘을 날지 못하는 설정이 생겼다면?


그냥 게임 중에 비행 알고리즘을 대체 해주면 끝입니다^^


이처럼 언제든지 알고리즘이 변경되어도 프로세스의 큰 틀을 바꾸지 않고도 유연하게 대체할 수 있죠.



오늘 만들어 본 드래곤볼 게임의 실행 파일과 전체 코드는 아래 링크에서 다운 받으시면 됩니다.


1_StretegyPatternStudy.zip


꼭 첨부된 파일을 받아 전체 코드를 확인하고 테스트 해보며 철저한 복습 부탁드립니다^^



디자인 패턴을 공부함에 있어 가장 중요한 것은 디자인 패턴의 정의를 달달 외우는 것이 아니고


이럴 때 이런 패턴을 사용했더니 이런 장단점이 있었다를 이해하는 것이 중요하다고 생각합니다.


앞으로도 이러한 관점에서 보다 이해하기 쉬운 방법으로 스터디를 진행해보려고 합니다.


많은 응원 부탁드려요. 여기까지 연두아빠였습니다.


공감() 및 댓글은 글쓴이에게 커다란 힘이 됩니다.


길지 않은 이 글을 쓰는데 나름 몇 시간이 걸렸어요^^;


저에게 1초만 시간을 내주셔서 공감 버튼 꾸욱 눌러주세요^^


제 블로그를 방문해 주신 모든 분들 사랑합니다^^


이상입니다. 감사합니다!



이 글을 공유하기

댓글

Designed by CMSFactory.NET