프로그래밍 일반/디자인 패턴

컴포지트 패턴(Composite Pattern)

지노윈 2019. 12. 8. 16:47
반응형

스타크래프트의 부대 지정을 상상하면 됩니다. 끝~~

컴포지트 패턴은 어떤 객체들의 집합에 대해 개별 인터페이스를 동일하게 가져갈 수 있게하는 것입니다.

배열에 기반한 속성

크리처의 속성을 배열로 구현하면 작성하기 쉽고 유지 보수 하기도 쉽다.

 

힘, 민첩, 지능을 가지는 크리쳐의 능력 들의 통계를 내는 구현입니다.

struct Creature
{
  int strength, agility, intelligence;
 
  int sum() const {
    return strength + agility + intelligence;
  }
  double average() const {
    return sum()/3.0;
  }
  int max() const {
    return max(max(strength, agility), intelligence);
  }
}
  • 크리처의 속성이 늘어 날때 마다 sum, average, max 구현을 모두 수정해야합니다.
  • 구현 도중에 구현을 누락하는 실수를 할 수도 있습니다.

좀더 우화하게 수정할 수 있는 방법이 바로 "배열 기반 속성"입니다.

struct Creature
{
  enum Abilities { str, agl, intl, count };
  array<int, count> abilities;
 
  int sum() const {
    return accumulate(abilities.begin(), abilities.end(), 0);
  }
  double average() const {
    return sum() / (double)count;
  }
  int max() const {
    return *max_element(abilities.begin(), abilities.end());
  }
}


그래픽 객체의 그룹핑

서로 다른 객체들을 드래그해서 여러 개 선택해서 마치 하나의 객체처럼 다룹니다.

개별 객체를 렌더링 할 수도 있고 여러 개의 도형을 하나의 그룹으로 렌더링할 수도 있습니다.

struct GraphicObject
{
  virtual void draw() = 0;
};
 
struct Circle : GraphicObject
{
  void draw() override
  {
    std::cout << "Circle" << std::endl;
  }
};
 
struct Group : GraphicObject
{
  std::string name;
 
  explicit Group(const std::string& name) : name{name}  { }
 
  void draw() override
  {
    for (auto&& o : objects)
      o->draw();
  }
 
  std::vector<GraphicObject*> objects;
};

GraphicObject 인터페이스를 구현하여 개별 원이든 그룹 객체든 draw() 함수를 구현하여 그립니다.

Group root("root");
Circle c1, c2;
root.objects.push_back(&c1);
 
Group subgroup("sub");
subgroup.objects.push_back(&c2);
 
root.objects.push_back(&subgroup);
 
root.draw();


뉴럴 네트워크

뉴럴 네트워크의 중심 개념은 뉴런이며 뉴런은 함수와도 같습니다.

어떤 입력에 대해 어떤 출력을 냅니다. 그 출력을 다른 뉴런의 입력으로 연결할 수 있고 그러한 연결이 모여 뉴럴 네트워크를 이룹니다.

 

뉴런과 뉴련의 연결 구현입니다.

struct Neuron : SomeNeurons<Neuron>
{
  vector<Neuron*> in, out;
  unsigned int id;
 
  Neuron()
  {
    static int id = 1;
    this->id = id++;
  }
};
 
template<> void connect_to<Neuron>(Neuron& other)
{
  out.push_back(&other);
  other.in.push_back(this);
}


뉴런과 뉴련 레이어 2개의 종류를 연결하려면 4종류의(뉴런+뉴런, 뉴런+레이어, 레이어+뉴런, 레이어+레이어) connect_to를 구현해야 합니다.

만약 3종류의 연결이라면 9개의 연결을 구현해야 합니다. 이효율적이고 더 나은 방법이 필요합니다.

 

일일이 연결 함수를 구현하는 대신, 베이스 클래스에 연결 함수를 만들고 다중 상속을 이용합니다.

template <typename Self>
struct SomeNeurons
{
  template <typename T> void connect_to(T& other) // 베이스 클래스의 연결 함수
  {
    for (Neuron& from : *static_cast<Self*>(this))
    {
      for (Neuron& to : other)
      {
        from.out.push_back(&to);
        to.in.push_back(&from);
      }
    }
  }
};
 
struct Neuron : SomeNeurons<Neuron>       // 베이스 상속
{
  vector<Neuron*> in, out;
  unsigned int id;
 
  Neuron()
  {
    static int id = 1;
    this->id = id++;
  }
  Neuron* begin() { return this; }
  Neuron* end() { return this + 1; }
};
 
struct NeuronLayer : vector<Neuron>, SomeNeurons<NeuronLayer>   // 베이스와 vector 다중 상속
{
  NeuronLayer(int count)
  {
    while (count-- > 0)
      emplace_back(Neuron{});
  }
};
Neuron neuron, neuron2;
NeuronLayer layer{ 5 }, layer2{ 2 };
 
neuron.connect_to(neuron2);
neuron.connect_to(layer);
layer.connect_to(neuron);
layer.connect_to(layer2);

 

[독서 리뷰] - 모던 C++ 디자인 패턴

 

모던 C++ 디자인 패턴

[객체 지향 프로그래밍/디자인 패턴] - 빌더 패턴(Builder Pattern) [분류 전체보기] - 디자인 패턴 [객체 지향 프로그래밍/디자인 패턴] - 팩토리 패턴(Factory pattern) [객체 지향 프로그래밍/디자인 패턴] -..

devjino.tistory.com

코드 참고 : https://cpp-design-patterns.readthedocs.io/en/latest/
 

Welcome to C++ Design Patterns’s documentation! — C++ Design Patterns 0.0.1 documentation

© Copyright 2018, Hans-J. Schmid Revision 786c83f8.

cpp-design-patterns.readthedocs.io