Pola desain untuk perilaku polimorfik sambil memungkinkan pemisahan perpustakaan

10

Katakanlah saya memiliki hirarki dari Itemkelas: Rectangle, Circle, Triangle. Saya ingin dapat menggambar mereka, jadi kemungkinan pertama saya adalah menambahkan Draw()metode virtual untuk masing-masing:

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

Namun, saya ingin membagi fungsionalitas menggambar ke pustaka Draw yang terpisah sementara Core library hanya berisi representasi dasar. Ada beberapa kemungkinan yang dapat saya pikirkan:

1 - A DrawManageryang mengambil daftar Items dan harus digunakan dynamic_cast<>untuk mengetahui apa yang harus dilakukan:

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

Ini tidak ideal karena bergantung pada RTTI dan memaksa satu kelas untuk mengetahui semua item dalam hierarki.

2 - Pendekatan lain adalah menunda menarik tanggung jawab ke ItemDrawerhierarki ( RectangleDrawer, dll):

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

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

Ini mencapai pemisahan kekhawatiran antara representasi dasar dari Item dan kode untuk menggambar. Masalahnya adalah bahwa kelas Item tergantung pada kelas menggambar.

Bagaimana saya bisa memisahkan kode gambar ini menjadi perpustakaan yang terpisah? Apakah solusi untuk Item mengembalikan kelas deskripsi dari pabrik? Namun, bagaimana ini dapat didefinisikan sehingga perpustakaan inti tidak bergantung pada perpustakaan undian?

the_mandrill
sumber

Jawaban:

3

Lihatlah pola pengunjung .

Maksudnya adalah untuk memisahkan suatu algoritma (dalam kasus Anda menggambar) dari suatu struktur objek di mana ia beroperasi (item).

Jadi contoh sederhana untuk masalah Anda adalah:

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;
}
kentang panas
sumber
Menarik - Saya tidak pernah berpikir tentang menggunakan pengunjung dengan kelebihan untuk mencapai polimorfisme. Akankah ini membebani dengan benar pada saat runtime?
the_mandrill
Ya itu akan kelebihan beban dengan benar. Lihat jawaban yang diedit
hotpotato
Saya dapat melihat bahwa ini bekerja. Sayang sekali bahwa setiap kelas turunan harus menerapkan visit()metode ini.
the_mandrill
Ini mendorong saya untuk melakukan pencarian lebih banyak dan saya sekarang menemukan penerapan pola Pengunjung Loki yang tampaknya persis seperti yang saya cari! Saya terbiasa dengan pola pengunjung dalam hal mengunjungi node di pohon, tetapi tidak memikirkannya secara lebih abstrak.
the_mandrill
2

Perpecahan yang Anda jelaskan - menjadi dua hierarki paralel - pada dasarnya adalah pola jembatan .

Anda khawatir itu membuat abstraksi yang disempurnakan (Rectangle dll) bergantung pada implementasinya (RectangleDrawer), tapi saya tidak yakin bagaimana Anda bisa menghindarinya sepenuhnya.

Anda tentu dapat memberikan pabrik abstrak untuk memecah ketergantungan itu, tetapi Anda masih perlu beberapa cara untuk memberi tahu pabrik subclass mana yang akan dibuat, dan subclass itu masih tergantung pada implementasi yang disempurnakan khusus. Yaitu, bahkan jika Rectangle tidak bergantung pada RectangleDrawer, masih perlu cara untuk memberitahu pabrik untuk membangunnya, dan RectangleDrawer sendiri masih bergantung pada Rectangle.

Juga patut ditanyakan apakah ini abstraksi yang bermanfaat. Apakah menggambar persegi panjang begitu keras sehingga layak dimasukkan ke kelas differnt, atau apakah Anda benar-benar mencoba untuk abstrak perpustakaan grafik?

Tak berguna
sumber
Saya pikir saya telah melihat pola itu sebelum memiliki hierarki paralel - terima kasih karena telah menyibukkan pikiran saya tentang hal itu. Meskipun saya ingin abstrak perpustakaan grafis. Saya tidak ingin perpustakaan inti harus bergantung pada perpustakaan grafis. Katakanlah aplikasi inti mengelola objek bentuk dan pustaka untuk menampilkannya adalah tambahan. Ingatlah bahwa ini sebenarnya bukan aplikasi untuk menggambar persegi panjang - Saya hanya menggunakannya sebagai metafora.
the_mandrill
Apa yang saya pikirkan adalah RectangleDrawer, CircleDrawerdll. Mungkin bukan cara yang paling berguna untuk abstrak perpustakaan grafik. Jika, sebaliknya, Anda mengekspos sekumpulan primitif melalui antarmuka abstrak biasa, Anda masih memutus ketergantungan.
berguna
1

Lihatlah Polimorfisme Berbasis Semantik Nilai dan Konsep .

Karya ini telah mengubah cara saya memandang polimorfisme. Kami telah menggunakan sistem ini untuk memberikan pengaruh besar pada situasi yang berulang.

Bill Door
sumber
Ini terlihat menarik - sepertinya sedang menuju ke arah yang saya inginkan
the_mandrill
1
Tautan rusak. Inilah sebabnya mengapa jawaban yang pada dasarnya hanya tautan tidak disarankan.
ajb
0

Alasan mengapa Anda mengalami masalah adalah karena benda tidak seharusnya menggambar sendiri. Menggambar sesuatu dengan benar tergantung pada satu miliar keping negara dan Rectangle tidak akan memiliki salah satu keping itu. Anda harus memiliki objek grafis sentral yang mewakili keadaan inti seperti backbuffer / depth buffer / shaders / etc, dan kemudian berikan metode Draw ().

DeadMG
sumber
Saya setuju bahwa objek tidak boleh menggambar diri mereka sendiri, karena itu keinginan untuk memindahkan kemampuan itu ke kelas yang berbeda untuk mencapai pemisahan keprihatinan. Misalkan kelas-kelas ini Dogatau Cat- objek yang bertanggung jawab untuk menggambar mereka jauh lebih kompleks daripada menggambar persegi panjang.
the_mandrill