라이브러리 분리를 허용하면서 다형성 동작을위한 디자인 패턴


10

Item클래스 의 계층 구조가 있다고 가정 해 봅시다 Rectangle, Circle, Triangle. 나는 그것들을 그릴 수 있기를 원하므로 첫 번째 가능성은 Draw()각각에 가상 방법을 추가하는 것입니다 .

class Item {
public:
   virtual ~Item();
   virtual void Draw() =0; 
};

그러나 Core 라이브러리에는 기본 표현 만 포함되어 있지만 그리기 기능을 별도의 Draw 라이브러리로 분할하고 싶습니다. 내가 생각할 수있는 몇 가지 가능성이 있습니다.

1- DrawManager의 목록을 가져 와서 수행 할 작업을 해결하기 Item위해 사용해야 하는 A dynamic_cast<>:

class DrawManager {
  void draw(ItemList& items) {
    FOREACH(Item* item, items) {
       if (dynamic_cast<Rectangle*>(item)) {
          drawRectangle();
       } else if (dynamic_cast<Circle*>(item)) {
          drawCircle();
       } ....
    }
  }
};

이것은 RTTI에 의존하고 한 클래스가 계층 구조의 모든 항목을 인식하도록 강요하기 때문에 이상적이지 않습니다.

2-다른 접근 방식은 도면 책임을 ItemDrawer계층 구조 ( RectangleDrawer) 등에 지연시키는 것입니다 .

class Item {
   virtual Drawer* GetDrawer() =0;
}

class Rectangle : public Item {
public:
   virtual Drawer* GetDrawer() {return new RectangleDrawer(this); }
}

이것은 아이템의 기본 표현과 드로잉 코드 사이의 관심사 분리를 달성합니다. 문제는 Item 클래스가 드로잉 클래스에 의존한다는 것입니다.

이 도면 코드를 별도의 라이브러리로 분리하려면 어떻게해야합니까? 항목에 대한 솔루션이 설명의 팩토리 클래스를 리턴합니까? 그러나 Core 라이브러리가 Draw 라이브러리에 종속되지 않도록 어떻게 정의 할 수 있습니까?

답변:


3

상기 살펴보세요 방문자 패턴을 .

그 목적은 알고리즘 (귀하의 도면에서)이 작동하는 객체 구조 (항목)와 알고리즘을 분리하는 것입니다.

따라서 문제의 간단한 예는 다음과 같습니다.

class Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};

class Rectangle : public Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};

class Circle : public Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};


class Visitable
{
public:
    virtual void accept(Rectangle& r);
    virtual void accept(Circle& c);
};

class Drawer : public Visitable
{
public:
    void accept(Rectangle& r)
    {
        // draw rectangle
    }   
    void accept(Circle& c)
    {
        // draw circle
    }
};


int main()
{
    Item* i1 = new Circle;
    Item* i2 = new Rectangle;

    Drawer d;
    i1->visit(d); // Draw circle
    i2->visit(d); // Draw rectangle

    return 1;
}

흥미 롭다-나는 다형성을 달성하기 위해 과부하가 걸린 방문자를 사용하는 것에 대해 결코 생각하지 않았습니다. 그래도 런타임에 올바르게 과부하됩니까?
the_mandrill

예, 올바르게 과부하됩니다. 수정 된 답변보기
hotpotato

이것이 작동한다는 것을 알 수 있습니다. 파생 된 각 클래스가 visit()메소드 를 구현해야한다는 것은 부끄러운 일 입니다.
the_mandrill

이로 인해 좀 더 검색을하게되었으며, 지금 내가 찾던 Loki 's Visitor 패턴의 구현을 찾았습니다 . 나는 나무에서 노드를 방문하는 관점에서 방문자 패턴에 익숙했지만 더 추상적 인 방식으로 생각하지 않았습니다.
the_mandrill

2

두 개의 병렬 계층으로 설명하는 분할은 기본적으로 브리지 패턴 입니다.

당신은 그것이하게 걱정 세련된 추상화 온 의존 (사각형 등) 구현 (RectangleDrawer을),하지만 난 당신이 완전히 그것을 피할 수있는 방법이 아니에요.

의존성을 깨뜨리기 위해 추상 팩토리를 제공 할 수는 있지만, 팩토리에 어떤 서브 클래스를 생성 할 것인지 알려주는 방법이 여전히 필요하며, 그 서브 클래스는 여전히 구체화 된 특정 구현에 의존합니다. 즉, Rectangle이 RectangleDrawer에 의존하지 않더라도 팩토리에 빌드하도록 팩토리에 알리는 방법이 필요하며 RectangleDrawer 자체는 여전히 Rectangle에 의존합니다.

이것이 유용한 추상화인지 물어볼 가치가 있습니다. 사각형을 너무 세게 그려 다른 클래스에 넣을 가치가 있습니까? 아니면 실제로 그래픽 라이브러리를 추상화하려고합니까?


병렬 계층 구조를 갖기 전에 그 패턴을 본 적이 있다고 생각했습니다. 그래도 그래픽 라이브러리를 추상화하고 싶습니다. 핵심 라이브러리가 그래픽 라이브러리에 의존하고 싶지는 않습니다. 핵심 응용 프로그램에서 모양 개체를 관리하고이를 표시하는 라이브러리가 애드온이라고 가정 해 봅시다. 이것은 실제로 사각형을 그리는 응용 프로그램이 아니라는 것을 명심하십시오-나는 단지 그것을 은유로 사용하고 있습니다.
the_mandrill

내가 생각하고하는 것은 즉 RectangleDrawer, CircleDrawer추상적 인 그래픽 라이브러리에 가장 유용한 방법이 될 수 없습니다 등. 대신 일반 추상 인터페이스를 통해 프리미티브 세트를 노출하는 경우 여전히 종속성이 손상됩니다.
쓸모없는

1

Value Semantics and Concepts-based Polymorphism을 살펴보십시오 .

이 작품은 내가 다형성을 바라 보는 방식을 바 꾸었습니다. 우리는이 시스템을 반복 된 상황에서 큰 영향을 미쳤습니다.


내가 좋아하는 방향으로 가고있는 것 같습니다
the_mandrill

1
링크가 끊어졌습니다. 이것이 본질적으로 링크 전용 인 답변이 권장되지 않는 이유입니다.
ajb

0

문제가 발생하는 이유는 객체가 스스로 그려지지 않아야하기 때문입니다. 무언가를 올바르게 그리는 것은 10 억 개의 상태에 달려 있으며 Rectangle은 그러한 부분을 가지지 않을 것입니다. backbuffer / depth buffer / shaders / etc와 같은 핵심 상태를 나타내는 중앙 그래픽 객체가 있어야하며 Draw () 메서드를 제공해야합니다.


나는 사물이 스스로를 끌어 당기지 말아야한다는 것에 동의한다. 그러므로 그 능력을 다른 계급으로 옮겨서 관심사를 분리하려는 욕구. 이 클래스들이 Dog또는 Cat그것들을 담당하는 객체가 사각형을 그리는 것보다 훨씬 더 복잡 하다고 가정 해 봅시다 .
the_mandrill
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.