Kütüphane ayrımına izin verirken polimorfik davranış için tasarım deseni


10

Diyelim ben bir hiyerarşi var ki Itemsınıflar: Rectangle, Circle, Triangle. Onları çizmek istiyorum, bu yüzden ilk olasılık Draw()her birine sanal bir yöntem eklemek :

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

Ancak, Çekirdek kitaplığı yalnızca temel gösterimleri içerirken çizim işlevselliğini ayrı bir Draw kitaplığına bölmek istiyorum. Düşünebileceğim birkaç olasılık var:

1 - Bir s DrawManagerlistesini alan Itemve dynamic_cast<>ne yapılacağını bulmak için kullanması gereken bir:

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

RTTI'ye bağlı olduğu ve bir sınıfı hiyerarşideki tüm öğelerden haberdar olmaya zorladığı için bu ideal değildir.

2 - Diğer yaklaşım, çizim sorumluluğunu bir ItemDrawerhiyerarşiye ( RectangleDrawervb.) Ertelemektir:

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

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

Bu, Maddelerin temel temsili ile çizim kodu arasındaki endişelerin ayrılmasını sağlar. Ancak sorun Item sınıflarının çizim sınıflarına bağımlı olmasıdır.

Bu çizim kodunu ayrı bir kütüphaneye nasıl ayırabilirim? Maddelerin bir tanımın fabrika sınıfına dönmesi için bir çözüm mü? Ancak, çekirdek kitaplığın Draw kitaplığına bağımlı olmaması için bu nasıl tanımlanabilir?

Yanıtlar:


3

Ziyaretçi desenine bir göz atın .

Amacı, bir algoritmayı (sizin durumunuzda) üzerinde çalıştığı nesne yapısından (öğeler) ayırmaktır.

Yani probleminiz için basit bir örnek:

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;
}

İlginç - Ben polimorfizm elde etmek için aşırı yüklenen bir ziyaretçi kullanmayı hiç düşünmemiştim. Bu çalışma zamanında doğru aşırı yüklenir mi?
the_mandrill

Evet, aşırı yüklenir. Düzenlenmiş cevaba bakın
hotpotato

Bunun işe yaradığını görebiliyorum. Ancak her türetilmiş sınıfın visit()yöntemi uygulaması bir utançtır .
the_mandrill

Bu, beni biraz daha fazla arama yapmaya yöneltti ve şimdi tam olarak aradığım şey gibi görünen Loki'nin Ziyaretçi kalıbının uygulanmasını buldum ! Ziyaretçi desenini ağaçlardaki düğümleri ziyaret etme konusunda tanıyordum, ancak daha soyut bir şekilde düşünmemiştim.
the_mandrill

2

Tanımladığınız bölünme - iki paralel hiyerarşiye - aslında köprü modelidir .

Rafine edilmiş soyutlamayı (Dikdörtgen vb.) Uygulamaya (RectangleDrawer) bağımlı hale getirdiğinden endişeleniyorsunuz , ancak bunu nasıl tamamen önleyebileceğinizden emin değilim.

Bu bağımlılığı kırmak için kesinlikle soyut bir fabrika sağlayabilirsiniz, ancak yine de fabrikaya hangi alt sınıfı yaratacağını ve bu alt sınıfın yine de belirli rafine uygulamaya bağlı olduğunu söylemenin bir yolunu bulmanız gerekir. Yani, Rectangle RectangleDrawer'a bağlı olmasa bile, yine de fabrikaya bir tane oluşturmasını söylemenin bir yolu gerekir ve RectangleDrawer'ın kendisi de Dikdörtgen'e bağlıdır.

Bunun yararlı bir soyutlama olup olmadığını sormaya değer. Dikdörtgen çizmek o kadar zor mu, farklı bir sınıfa koymaya değer mi, yoksa gerçekten grafik kütüphanesini soyutlamaya mı çalışıyorsunuz?


Paralel hiyerarşilere sahip olmadan önce bu modeli gördüğümü sanıyordum. Grafik kütüphanesini soyutlamak istiyorum. Çekirdek kütüphanenin grafik kütüphanesine bağlı olmasını istemiyorum. Çekirdek uygulamanın şekil nesnelerini yönettiğini ve bunları görüntülemek için kütüphanenin bir eklenti olduğunu varsayalım. Bunun gerçekten dikdörtgen çizmek için bir uygulama olmadığını aklınızda bulundurun - sadece bir metafor olarak kullanıyorum.
the_mandrill

Ne düşünme olmam RectangleDrawer, CircleDrawersoyut grafik kütüphanesine en yararlı şekilde olmayabilir vb. Bunun yerine, düzenli bir soyut arabirim aracılığıyla bir dizi ilkel ortaya çıkarırsanız, yine de bağımlılığı kırarsınız.
Yararsız


0

Sorun yaşamanızın nedeni, nesnelerin kendilerini çizmemesidir. Bir şeyi doğru şekilde çizmek bir milyar parça devlete bağlıdır ve bir Dikdörtgen bu parçalardan hiçbirine sahip olmayacaktır. Backbuffer / derinlik tamponu / gölgelendiriciler / vb. Gibi temel durumu temsil eden merkezi bir grafik nesneniz olmalı ve daha sonra bu nesneye bir Draw () yöntemi vermelisiniz.


Nesnelerin kendilerini çizmemesi gerektiğine katılıyorum, bu nedenle endişelerin ayrılmasını sağlamak için bu yeteneği farklı bir sınıfa taşıma arzusu. Bu sınıfların olduğunu varsayalım Dogveya Cat- onları çizmekten sorumlu nesne, bir dikdörtgen çizmekten çok daha karmaşıktır.
the_mandrill
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.