데코레이터 패턴을 이용한 집행검 인챈트 시스템 개발 #2

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


데코레이터 패턴 관련 스터디 두번째 시간이네요.


지난 포스팅에 이어서 이번에는 실제로 스릴 넘치는 인챈트 시스템 개발을 해보겠습니다.



지난 번에 말씀 드렸다 시피 진명황의 집행검이 아니고 연두아빠의 집행검이구요.


모조품이니 안심하고 마음껏 인챈트를 시도해 볼 수 있을 것 같습니다.



지난 포스팅에서 언급되었던 인챈트 시스템의 기획은 아래와 같습니다.


  • 인챈트 속성 값에 따라 각기 다른 화려한 이펙트가 연출 된다.
  • 각각의 속성에는 특별한 추가 옵션이 붙는다.
  • 속성마다 가치가 다르며, 검의 가치에 모든 속성의 가치를 더한 것이 최종 가치.


이 정도였죠?


일단 콘솔 환경에서 개발하는 것이니, 첫번째 화려한 이팩트는 스킵합시다^^;


추가 옵션 들도 당장 어디에 쓸 수 있는 것들이 아니니 최종적인 가치를 계산하는 것으로 퉁칠께요.


최대한 간단히 데코레이터 패턴의 공부하기 좋은 에센셜 코드를 만들어 봅시다.


자, 그럼 위의 기획 내용들을 바탕으로 한번 클래스 구조를 디자인 해보면 아래와 같습니다.



이제 어떤 식으로 디자인 되는지 감이 오시나요?


집행검(EnforcementSword) 클래스인챈트 속성(EnchantAttributes) 클래스가 존재하고요.


둘 다 IEnchantable이라는 인터페이스로 묶여서 이 인터페이스를 상속받게 됩니다.


EnchantAttributes를 상속받은 객체들은 인챈트 속성들이고, 집행검 클래스를 꾸며주는 역할을 하죠.


SparklingEnchant를 이용해 장식을 하면, 번쩍이는 집행검이 되는 것이고,


여기에 다시 LegendaryEnchant를 이용해 장식을 하면 전설의 번쩍이는 집행검이 되는 식의 구성입니다.



뭔가 좀더 실제 같이 구현하려면, 집행검 외에도 다른 검들이 존재 할테니...


무기(Weapon) 혹은 검(Sword) 등으로 묶어주는 부모 클래스가 있어야 하겠죠.


그러나 역시나 우리는 최대한 간단한 코드를 통해 데코레이터 패턴을 학습해야 하기에 뺐습니다.


어쨋든 위 그림과 같은 형태를 코드로 옮겨보면 다음과 같습니다.


< IEnchantable.cs >

public interface IEnchantable

{

    int Rank { get; }

    int Worth { get; }

    string Description { get; }

}


IEnchantable 인터페이스입니다. 간단하죠?


함수로 GetRank(), GetWorth(), GetDescription() 등과 같이 구현할 수도 있었겠지만...


C# 느낌을 좀 더 내보려고 Auto Property로 만들어 보았습니다. 함수를 이용해도 무방합니다.


그냥 C#에선 인터페이스를 이렇게 구성할 수도 있다. 정도만 확인하고 넘어가시면 될 것 같네요.


< EnforcementSword.cs >

public class EnforcementSword : IEnchantable

{

    int Rank

    {

        get return 0; }

    }


    int Worth

    {

        get return 1000; }

    }


    string Description

    {

        get return "연두아빠의 집행검"}

    }

}


IEnchantable을 상속받는 클래스들은 위 코드 처럼 Auto Property를 구현해주기만 하면 됩니다.


< EnchantAttributes.cs >

public abstract class EnchantAttributes : IEnchantable

{

    IEnchantable Target = null;


    public EnchantAttributes(IEnchantable target)

    {

        Target = target;

    }


    int Rank

    {

        get return Target.Rank}

    }


    int Worth

    {

        get return Target.Worth}

    }


    string Description

    {

        get return Target.Description; }

    }

}


인챈트 속성들을 수식해주기 위한 EnchantAttributes 클래스 거의 역시 유사합니다.


다만, 추상 클래스로 선언 되었고 IEnchantable을 Target으로 가지고 있는 것이 다르네요.


생성자에서는 IEnchantable을 인자로 받아서 target으로 저장해 줍니다.



이제 데코레이터 패턴을 구현하기 위한 모든 준비 과정이 끝났습니다.


EnchantAttributes를 상속받는 장식자들만 구현하면 됩니다.


지면 관계상 인챈트 속성 클래스 중 하나만 살펴보겠습니다 사실상 전부 거의 유사하니까요.


< LegendaryEnchant.cs >

public abstract class LegendaryEnchant : EnchantAttributes

{

    public LegendaryEnchant(IEnchantable target) : base (target) {}


    int Rank

    {

        get return 300 + base.Rank}

    }


    int Worth

    {

        get return 1000000 + base.Worth; }

    }


    string Description

    {

        get return "전설의 " base.Description; }

    }

}


인챈트 속성 중 전설의 속성을 인챈트 할 수 있는 LegendaryEnchant 클래스입니다.


대부분의 기능은 EnchantAttributes에 이미 구현되어 있는 형태이기 때문에...


생성자부터 Property까지 대부분 부모 클래스의 것을 참조하고 있습니다.


다만 Property 구현부들을 보면, 300 + base.Rank와 같이 처리 되고 있는 걸 볼 수 있는데요.


이런 식으로 처리를 함으로써 마치 링크드 리스트를 탐색하는 것과 유사한 방식으로


모든 장식자들을 제일 바깥 장식자에서 가장 안쪽 장식자 객체까지 차례로 탐색하며 처리합니다.



public void DecorationTest()

{

    IEnchantable sword = new EnforcementSword ();

    sword = new SuperEnchant (sword);          // 슈퍼 인챈트.

    sword = new SparklingEnchant (sword);      // 번쩍이는 인챈트.

    sword = new LegendaryEnchant (sword);    // 전설의 인챈트.

    Console.WriteLine(sword.Description);        // 결과: 전설의 번쩍이는 슈퍼 인챈트.

    Console.ReadKey();

}


위의 코드만 보면 대충 어떤 식으로 장식이 되는 지 이해가 가실 거라고 생각하지만,


친절한 연두아빠니까 한번 더 그림으로 쉽게 풀어 설명을 드려보겠습니다.



이 정도 도식화면 충분히 이해하실 수 있으실 꺼라고 믿어보겠습니다.


제일 처음에 생성한 집행검 클래스 EnforcementSwordIEnchantable 객체에 들어가게 되고,


이후부터는 장식자 객체들을 생성하며 이 집행검 클래스를 꾸며주게 되는 구조입니다.


다음에 생성한 SuperEnchant 객체의 target에서는 EnforcementSword 객체를 가리키고,


다음에 생성한 SparklingEnchant 클래스의 target에서는 SuperEnchant 객체를 가리키고,


마지막 LegendaryEnchant 클래스의 target에서는 SparklingEnchant 객체를 가리키는 식으로요.



그래서 최종 완성된 객체에서 Description을 호출하게 되면, 화살표를 역으로 따라가며,


각자의 가리키고 있는 target 들을 역참조하여 최종 검 이름이 출력이 되게 되는 것이죠.


이런 식으로 최종 완성된 코드를 실행해 보면 다음과 같습니다.



간단히 인챈트 시도 여부를 물어보는 첫번째 화면이 나오구요.


인챈트를 할 것인지, 중단을 할 것인지 택하면 됩니다.


인챈트를 중단하면 현재까지 만들어진 아이템을 획득하며 게임이 끝납니다.


인챈트를 시도하겠다고 하면 다음과 같이 인챈트를 시도합니다.



완전 흥미진진하고 멋진 인챈트 시스템이네요.


물론 매번 성공하는 것은 아닙니다.



위와 같이 대장장이의 손이 미끄러지며 인챈트에 실패하기도 하니 더욱 스릴 넘치네요.


오늘도 역시 전체 코드와 실행 파일을 첨부해드리니 아래 링크에서 다운 받으시면 됩니다.


2_DecoratorPatternStudy.zip


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



여기까지 장식자 패턴, 데코레이터 패턴(Decorator Pattern)을 공부해 보았는데요.


사실 약간의 어거지로 데코레이터 패턴에 알맞는 인챈트 시스템 기획을 만들긴 했습니다. 인정!


하지만 어쨋든 나름 재밌고 임팩트 넘치게 데코레이터 패턴을 공부해 볼 수 있었지 않았나요.... ^^;



물론 실제 인챈트 시스템을 개발할때 데코레이터 패턴을 사용한다 라고 생각하시면 큰일 나겠죠.


우리는 오늘 데코레이터 패턴을 이용해서 매우 재미있고 스릴 넘치는 인챈트 시스템을 만들어 보았지만,


사실 실제 게임 개발에서 데코레이터 패턴을 활용하기는 쉽지가 않습니다.



데코레이터 패턴은 클래스 디자인을 매우 유연하게 접근할 수 있다는 장점이 있는 반면에,


장식자 객체들이 많아질 수록 코드가 복잡해지고 가독성이 떨어진다는 단점이 있구요.


장식을 위한 모든 객체들을 일일히 별도의 클래스로 디자인해야 하기때문에


자잘한 기획 내용들 하나 하나를 클래스로 만들어야 하게 될 수도 있습니다.


방심하고 있다가는 이러한 이유들로 인해 곳곳에서 중복 코드들이 발생 될 수도 있구요.



그럼에도 불구하고 다양한 부가 기능들을 클래스 객체로 만들어서 주객체에 덮어 씌우는 식으로


제한 없는 확장이 가능하고,  코드 수정 없이 유연한 설계를 할 수 있는 아주 유용한 패턴 입니다.


데코레이터 패턴을 사용할 때는 모든 작업자들이 데코레이터 패턴을 사용 중임을 인지하고


조심해서 사용한다면 충분히 괜찮고 훌륭한 디자인 패턴이라는 것을 명심하시면서


오늘 스터디를 마치겠습니다.


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


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


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


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


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


이 글을 공유하기

댓글

Designed by CMSFactory.NET