Pola pengamat; mengetahui * apa * berubah?


10

Saya telah membuat dua kelas abstrak Subject dan Observer yang mendefinisikan antarmuka pola Observer klasik. Saya berasal dari mereka untuk menerapkan pola Observer. Seorang pengamat mungkin terlihat seperti ini:

void MyClass::Update(Subject *subject)
{
    if(subject == myService_)
    {
        DoSomething();
    }
    else if(subject == myOtherService_)
    {
        DoSomethingElse();
    }
}

Ini baik dan itu memberitahu saya siapa yang mengubah sesuatu. Namun, itu tidak memberi tahu saya apa yang berubah. Terkadang ini ok karena saya hanya akan meminta Subjek untuk data terbaru, tetapi di lain waktu saya perlu tahu apa yang sebenarnya berubah pada Subjek. Saya perhatikan di Jawa mereka memiliki metode notifyObservers () dan metode notifyObservers (Object arg) untuk menentukan rincian tentang apa yang berubah.

Dalam kasus saya, saya perlu tahu apakah salah satu dari beberapa tindakan berbeda terjadi pada subjek dan, jika itu tindakan tertentu, untuk mengetahui bilangan bulat yang terkait dengan tindakan itu.

Jadi pertanyaan saya adalah:

  1. apa cara C ++ untuk melewatkan argumen umum (seperti yang dilakukan Java)?
  2. Apakah Pengamat bahkan merupakan pola terbaik? Mungkin semacam sistem acara?

MEMPERBARUI

Saya menemukan artikel ini yang berbicara tentang templating pola Observer: Menerapkan pola Subjek / Observer dengan templat . Ini membuat saya bertanya-tanya apakah Anda bisa membuat templat argumen.

Saya menemukan pertanyaan overflow tumpukan ini yang berbicara tentang templating argumen: Pola Pengamat Subjek berbasis template - Haruskah saya menggunakan static_cast atau dynamic_cast . Namun, OP tampaknya memiliki masalah yang belum ada yang menjawab.

Hal lain yang bisa saya lakukan adalah mengubah metode Pembaruan untuk mengambil objek EventArg seperti pada:

void MyClass::Update(Subject *subject, EventArg arg)
{
  ...

Dan kemudian membuat subclass dari EventArg untuk data argumen tertentu, dan kemudian saya kira melemparkannya kembali ke subclass spesifik dalam metode pembaruan.

PEMBARUAN 2

Juga menemukan artikel, Saat membuat kerangka kerja c ++ berbasis pesan yang tidak sinkron; bagian 2 yang membahas perihal Subjek mengkomunikasikan perincian tentang apa yang berubah.

Saya sekarang serius mempertimbangkan untuk menggunakan Boost.Signals . Menggunakan pola pengamat saya sendiri masuk akal ketika itu sederhana, tetapi templating jenis dan argumen mulai menjadi rumit. Dan saya mungkin membutuhkan keamanan thread dari Boost.Signals2.

PEMBARUAN 3

Saya juga menemukan beberapa artikel menarik tentang pola pengamat:

Generalisasi Pengamat oleh Herb Sutter

Menerapkan Pola Pengamat di C ++ - Bagian 1

Pengalaman Menerapkan Pola Desain Pengamat (Bagian 2)

Pengalaman Menerapkan Pola Desain Pengamat (Bagian 3)

Namun, saya telah beralih implementasi saya untuk menggunakan Boost.Signals yang, walaupun mungkin sedikit membengkak untuk tujuan saya, berhasil bekerja. Dan mungkin kekhawatiran tentang kembung atau kecepatan tidak relevan.


Pertanyaan-pertanyaan dalam tubuh tampaknya tidak benar-benar cocok dengan judul Anda. Yang benar-benar inti dari pertanyaan itu?
Nicole

@Renesis: Saat ini saya menggunakan pola Observer seperti pada contoh kode di awal posting saya. Untuk kode yang saat ini saya kerjakan, ternyata saya perlu tahu secara spesifik apa yang berubah sehingga saya dapat bereaksi. Implementasi saya saat ini dari pola pengamat (yang merupakan standar) tidak memberikan informasi ini. Pertanyaan saya adalah bagaimana cara terbaik mendapatkan informasi tentang apa yang berubah.
Pengguna

@Renesis: Ini adalah utas forum yang menanyakan pertanyaan serupa kepada saya: gamedev.net/topic/497105-observer-pattern
Pengguna

Pembaruan iklan2: Saya pasti mendukung menggunakan Boost.Signals. Ini jauh lebih nyaman daripada menggulung sendiri. Ada juga libsigc ++ jika Anda menginginkan sesuatu yang lebih ringan hanya untuk tugas ini (Boost.Signals menggunakan banyak kode dari sisa Boost); itu bukan thread-safe sekalipun.
Jan Hudec

Mengenai masalah kecepatan: Saya tidak tahu secara spesifik seberapa cepat Boost. Sinyal, tapi itu hanya masalah ketika Anda memiliki sejumlah besar acara terbang di sekitar ...
Max

Jawaban:


4

Apakah C ++ atau JAVA, Pemberitahuan kepada pengamat dapat datang bersama dengan informasi tentang apa yang diubah. Metode notifyObservers (Object arg) yang sama juga dapat digunakan dalam C ++.

Secara umum, masalah akan tetap adalah bahwa mungkin ada banyak subjek yang dikirim ke satu atau beberapa pengamat dan karenanya, class argtidak dapat dikodekan dengan keras.

Biasanya, cara terbaik yang harus dilakukan adalah membuat arg dalam bentuk pesan umum / token yang membentuk tipe data yang sama untuk berbagai kelas tetapi nilainya berbeda untuk kelas yang diamati berbeda. Atau, jika semua nilai notifikasi tersebut diturunkan dari kelas pada beberapa kelas berbasis yang umum untuk semua.

Untuk pola pengamat, itu adalah penting bahwa Arg tipe data tidak sulit kode antara observee dan pengamat - yang lain itu adalah kopling yang membuat hal-hal sulit untuk berkembang.

EDIT
Jika Anda ingin pengamat itu tidak hanya mengamati tetapi juga perlu melakukan banyak tugas lain berdasarkan apa yang telah berubah , Anda juga dapat memeriksa pola Pengunjung . Dalam pola pengunjung, pengamat / pengunjung memanggil objek yang dimodifikasi dan karenanya tidak hanya bisa tahu apa modifikasi itu tetapi sebenarnya dapat bekerja di dalamnya


5
Jika pengamat menafsirkan argumen, ada adalah kopling, tidak peduli bagaimana Anda menyembunyikan jenis. Sebenarnya saya akan mengatakan itu lebih sulit untuk berkembang jika Anda lulus Object( void *, boost::anyatau sesuatu yang serupa generik) daripada jika Anda melewati tipe tertentu, karena dengan tipe spesifik Anda akan melihat pada waktu kompilasi bahwa sesuatu berubah, sedangkan dengan tipe generik itu akan mengkompilasi dan berhenti bekerja, karena pengamat tidak akan dapat bekerja dengan data aktual yang diteruskan.
Jan Hudec

@ JanHudec: Saya setuju dengan itu, tetapi apakah itu berarti Anda membuat satu sub-kelas Observer / Subjek yang ditetapkan untuk setiap argumen (yaitu, untuk setiap kasus penggunaan)?
Pengguna

@ JanHudec: kopling juga hanya satu arah. Subjek tidak tahu tentang pengamat. Ya pengamat tahu tentang subjek tetapi bukankah cara pengamat bekerja?
Pengguna

1
@ Pengguna: Ya, saya membuat antarmuka spesifik untuk setiap mata pelajaran dan setiap pengamat mengimplementasikan antarmuka mata pelajaran yang perlu diamati. Yah, semua bahasa yang saya gunakan memiliki pointer metode terikat dalam bahasa atau kerangka kerja (delegasi C #, C ++ 11 std::functionBoost boost::function, Gtk + GClosure, metode terikat python dll.), Jadi saya hanya mendefinisikan metode dengan tanda tangan yang sesuai dan meminta sistem untuk membuat aktual pengamat. Kopling ini memang hanya satu arah, subjek mendefinisikan antarmuka untuk pengamat, tetapi tidak memiliki gagasan tentang implementasinya.
Jan Hudec

0

Ada beberapa cara untuk mengirim argumen peristiwa umum "Suka Java" di C ++.

1) Nyatakan argumen event sebagai void * dan serahkan ke kelas yang tepat di event handler.

2) Nyatakan argumen event sebagai penunjuk / ref ke kelas / antarmuka baru seperti (sebagai tanggapan atas contoh Anda)

class GenericEventArgument
{
  virtual bool didAction1Happen() = 0;
  virtual int getActionInteger() = 0;
};

Dan mintalah kelas argumen acara yang sebenarnya berasal dari kelas ini.

Adapun pertanyaan Anda tentang pengamat berbasis template periksa

http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ


-1

Saya tidak tahu apakah ini nama kanonik, tetapi dari masa kecil saya Smalltalk saya ingat istilah "aspek" untuk mengidentifikasi apa yang telah berubah pada yang diamati.

Ini tidak serumit ide Anda tentang EventArg (dan mensubklasifikasikannya); itu hanya melewati string (bahkan bisa berupa konstanta integer) dari yang dapat diamati kepada pengamat.

Plus: Hanya ada dua metode sederhana ( update(observable)danupdateAspect(observable, aspect)

Minus: Pengamat mungkin harus meminta informasi lebih lanjut yang dapat diamati (yaitu, "bilangan bulat" Anda)

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.