รูปแบบการสังเกตการณ์; รู้ว่า * อะไร * เปลี่ยนไป?


10

ฉันได้สร้างคลาสนามธรรมสองวิชาและผู้สังเกตการณ์ที่กำหนดอินเทอร์เฟซรูปแบบการสังเกตคลาสสิก ฉันสืบทอดมาจากพวกเขาเพื่อใช้รูปแบบการสังเกตการณ์ ผู้สังเกตการณ์อาจมีลักษณะเช่นนี้:

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

นี่เป็นเรื่องปกติและบอกฉันว่าใครเปลี่ยนอะไร อย่างไรก็ตามมันไม่ได้บอกฉันว่ามีอะไรเปลี่ยนแปลง บางครั้งสิ่งนี้ก็โอเคเพราะฉันแค่จะสืบค้นเรื่องสำหรับข้อมูลล่าสุด แต่บางครั้งฉันก็ต้องรู้ว่าสิ่งที่เปลี่ยนแปลงไปในเรื่องนั้น ๆ ฉันสังเกตเห็นใน Java พวกเขามีทั้ง informObservers () วิธีการและalertObservers (Object ARG)วิธีการสันนิษฐานว่าจะระบุรายละเอียดเกี่ยวกับสิ่งที่เปลี่ยนแปลง

ในกรณีของฉันฉันจำเป็นต้องรู้ว่าหนึ่งในสองของการกระทำที่แตกต่างกันเกิดขึ้นในเรื่องและถ้าเป็นการกระทำที่เฉพาะเจาะจงเพื่อทราบจำนวนเต็มที่เกี่ยวข้องกับการกระทำนั้น

ดังนั้นคำถามของฉันคือ:

  1. วิธี C ++ ผ่านอาร์กิวเมนต์ทั่วไป (เช่น Java)
  2. ผู้สังเกตการณ์เป็นแบบแผนที่ดีที่สุดหรือไม่? บางทีระบบเหตุการณ์บางอย่าง?

UPDATE

ผมพบว่าบทความนี้ซึ่งพูดเกี่ยวกับรูปแบบ templating สังเกตการณ์: การนำเรื่องรูปแบบกับแม่แบบ / นักสังเกตการณ์ นี่ทำให้ฉันสงสัยว่าคุณสามารถโต้เถียงเรื่องแม่แบบได้หรือไม่

ผมพบว่าคำถามนี้กองล้นที่พูดเกี่ยวกับ templating อาร์กิวเมนต์: แม่แบบตามรูปแบบเรื่องสังเกตการณ์ - ฉันควรใช้ static_cast หรือ dynamic_cast อย่างไรก็ตาม OP ดูเหมือนว่าจะมีปัญหาที่ไม่มีใครตอบ

สิ่งอื่น ๆ ที่ฉันสามารถทำได้คือเปลี่ยนวิธีการอัปเดตเป็นวัตถุ EventArg เช่นเดียวกับใน:

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

จากนั้นสร้างคลาสย่อยของ EventArg สำหรับข้อมูลอาร์กิวเมนต์ที่เฉพาะเจาะจงแล้วฉันเดาว่ามันจะส่งกลับไปที่คลาสย่อยเฉพาะภายในวิธีการอัปเดต

อัพเดท 2

พบบทความเกี่ยวกับการสร้างเฟรมเวิร์ก c ++ แบบอะซิงโครนัสข้อความ ส่วนที่ 2ซึ่งกล่าวถึงการให้หัวเรื่องสื่อสารรายละเอียดเกี่ยวกับสิ่งที่เปลี่ยนแปลง

ตอนนี้ผมกำลังพิจารณาอย่างจริงจังโดยใช้Boost.Signals การใช้รูปแบบผู้สังเกตการณ์ของฉันเองนั้นสมเหตุสมผลเมื่อมันเรียบง่าย แต่การทำให้เท็มเพลตและการโต้แย้งเริ่มซับซ้อนขึ้น และฉันอาจต้องการความปลอดภัยของเธรดของ Boost.Signals2

อัพเดท 3

ฉันยังพบบทความที่น่าสนใจเกี่ยวกับรูปแบบการสังเกตการณ์:

การสังเกตผู้สังเกตการณ์โดย Herb Sutter

การนำรูปแบบการสังเกตไปใช้ใน C ++ - ตอนที่ 1

ประสบการณ์การใช้รูปแบบการออกแบบของผู้สังเกตการณ์ (ตอนที่ 2)

ประสบการณ์การใช้รูปแบบการออกแบบของผู้สังเกตการณ์ (ตอนที่ 3)

อย่างไรก็ตามฉันได้เปลี่ยนการใช้งานของฉันไปใช้ Boost.Signals ซึ่งในขณะที่อาจปูดป่องสำหรับวัตถุประสงค์ของฉันทำงานได้สำเร็จ และอาจมีความกังวลเกี่ยวกับการขยายตัวหรือความเร็วที่ไม่เกี่ยวข้อง


คำถามในร่างกายดูเหมือนจะไม่ตรงกับชื่อของคุณ อะไรคือแก่นแท้ของคำถาม?
นิโคล

@Renesis: ขณะนี้ฉันใช้รูปแบบการสังเกตการณ์ในตัวอย่างโค้ดตอนต้นโพสต์ของฉัน สำหรับรหัสที่ฉันกำลังทำงานอยู่มันกลับกลายเป็นว่าฉันจำเป็นต้องรู้ว่ามีอะไรเปลี่ยนแปลงไปบ้างเพื่อที่ฉันจะสามารถตอบสนองได้ การใช้รูปแบบผู้สังเกตการณ์ในปัจจุบันของฉัน (ซึ่งเป็นรูปแบบมาตรฐาน) ไม่ได้ให้ข้อมูลนี้ คำถามของฉันคือวิธีรับข้อมูลที่ดีที่สุดเกี่ยวกับสิ่งที่เปลี่ยนแปลงไป
ผู้ใช้

@Renesis: นี่คือกระทู้ฟอรัมที่ถามคำถามที่คล้ายกันกับของฉัน: gamedev.net/topic/497105-observer-pattern
ผู้ใช้

โฆษณา update2: ฉันสนับสนุนการใช้ Boost.Signals อย่างแน่นอน สะดวกกว่าการรีดด้วยตัวเอง นอกจากนี้ยังมีlibsigc ++หากคุณต้องการบางสิ่งที่มีน้ำหนักเบาสำหรับงานนี้เท่านั้น (Boost.Signals ใช้รหัสจำนวนมากจากส่วนที่เหลือของ Boost); มันไม่ปลอดภัยแม้ว่าเธรด
Jan Hudec

เกี่ยวกับปัญหาเรื่องความเร็ว: ฉันไม่รู้ว่าการเร่งความเร็วแบบเร่งด่วนเป็นอย่างไร แต่เป็นเรื่องที่น่ากังวลเมื่อคุณมีเหตุการณ์จำนวนมากบินวนอยู่รอบ ๆ ...
สูงสุด

คำตอบ:


4

ไม่ว่าจะเป็น C ++ หรือ JAVA การแจ้งเตือนไปยังผู้สังเกตการณ์สามารถมาพร้อมกับข้อมูลของสิ่งที่เปลี่ยนแปลงได้ เมธอดเดียวกัน alertObservers (Object arg) สามารถใช้ใน C ++ ได้เช่นกัน

โดยทั่วไปแล้วปัญหาจะยังคงอยู่คืออาจมีหลายวิชาที่ส่งไปยังผู้สังเกตการณ์หนึ่งคนหรือหลายคนและด้วยเหตุนี้จึงclass argไม่สามารถเข้ารหัสยาก

โดยปกติแล้ววิธีที่ดีที่สุดที่จะทำคือการหาเรื่องในรูปแบบของข้อความ / โทเค็นทั่วไปที่สร้างชนิดข้อมูลเดียวกันสำหรับคลาสต่าง ๆ แต่ค่าแตกต่างกันไปสำหรับคลาสที่สังเกตเห็นต่างกัน อีกทางหนึ่งถ้าค่าการแจ้งเตือนทั้งหมดนั้นได้รับมาจากคลาสในคลาสพื้นฐานบางตัวซึ่งเป็นเรื่องธรรมดาสำหรับทุกคน

สำหรับรูปแบบของผู้สังเกตการณ์เป็นสิ่งสำคัญที่ประเภทข้อมูลของ Arg นั้นไม่ได้เข้ารหัสอย่างหนักระหว่างผู้สังเกตการณ์และผู้สังเกตการณ์ - มิฉะนั้นมันเป็นข้อต่อที่ทำให้สิ่งต่าง ๆ ยากที่จะพัฒนาขึ้น

แก้ไข
หากคุณต้องการให้ผู้สังเกตการณ์ไม่เพียง แต่สังเกต แต่ยังต้องทำงานอื่น ๆ อีกมากมายตามสิ่งที่เปลี่ยนแปลงไปคุณสามารถตรวจสอบรูปแบบของผู้เยี่ยมชมได้ ในรูปแบบของผู้เยี่ยมชมผู้สังเกตการณ์ / ผู้เยี่ยมชมเรียกวัตถุที่ถูกดัดแปลงและด้วยเหตุนี้ไม่เพียง แต่สามารถรู้ได้ว่าการปรับเปลี่ยนคืออะไรแต่สามารถใช้งานได้จริง


5
หากผู้สังเกตการณ์ตีความข้อโต้แย้งนั้นจะมีการแต่งงานกันไม่ว่าคุณจะซ่อนประเภทนี้อย่างไร ในความเป็นจริงผมว่ามันเป็นมากขึ้นยากที่จะพัฒนาขึ้นถ้าคุณผ่านObject( void *, boost::anyหรืออะไรทำนองเดียวกันทั่วไป) กว่าถ้าคุณผ่านประเภทที่เฉพาะเจาะจงเพราะมีประเภทเฉพาะที่คุณจะเห็นที่รวบรวมเวลาว่าสิ่งที่เปลี่ยนแปลงในขณะที่มีประเภททั่วไปมัน จะรวบรวมและหยุดทำงานเนื่องจากผู้สังเกตการณ์จะไม่สามารถทำงานกับข้อมูลจริงที่ส่งผ่านได้
Jan Hudec

@JanHudec: ฉันเห็นด้วย แต่นั่นหมายความว่าคุณตั้งค่าคลาสย่อยของผู้สังเกตการณ์ / หัวเรื่องแบบครั้งเดียวเฉพาะสำหรับแต่ละอาร์กิวเมนต์ (เช่นสำหรับแต่ละกรณีการใช้งาน) หรือไม่
ผู้ใช้

@ JanHudec: การมีเพศสัมพันธ์ก็เป็นเพียงทางเดียว หัวเรื่องไม่มีความคิดเกี่ยวกับผู้สังเกตการณ์ ใช่ผู้สังเกตการณ์รู้เกี่ยวกับเรื่อง แต่ไม่ใช่ว่ารูปแบบการสังเกตการณ์ทำงานอย่างไร
ผู้ใช้

1
@ ผู้ใช้: ใช่ฉันสร้างอินเทอร์เฟซเฉพาะสำหรับแต่ละเรื่องและผู้สังเกตการณ์แต่ละคนใช้อินเทอร์เฟซของวัตถุที่ต้องสังเกต ทุกภาษาที่ฉันใช้มีพอยน์เตอร์ของเมธอดที่ถูกผูกไว้ในภาษาหรือเฟรมเวิร์ก (ผู้แทน C #, C ++ 11 std::functionBoost boost::function, Gtk + GClosure, ไพ ธ อนวิธีที่ถูกผูกไว้เป็นต้น) ดังนั้นฉันจึงกำหนดวิธีด้วยลายเซ็นที่เหมาะสม ผู้สังเกตการณ์ การมีเพศสัมพันธ์นั้นเป็นเพียงทางเดียวเท่านั้นหัวเรื่องกำหนดอินเทอร์เฟซสำหรับผู้สังเกตการณ์ แต่ไม่มีความคิดเกี่ยวกับการนำไปปฏิบัติ
Jan Hudec

0

มีสองสามวิธีในการส่งอาร์กิวเมนต์ทั่วไป "Java Like" ใน C ++

1) ประกาศอาร์กิวเมนต์ของเหตุการณ์เป็นโมฆะ * และส่งไปยังคลาสที่เหมาะสมในตัวจัดการเหตุการณ์

2) ประกาศอาร์กิวเมนต์ของเหตุการณ์เป็นตัวชี้ / อ้างอิงไปยังคลาส / อินเทอร์เฟซใหม่เช่น (เพื่อตอบสนองต่อตัวอย่างของคุณ)

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

และมีคลาสอาร์กิวเมนต์ของเหตุการณ์จริงที่เกิดขึ้นจากคลาสนี้

สำหรับคำถามของคุณเกี่ยวกับผู้สังเกตการณ์ที่ใช้เทมเพลตลองดู

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


-1

ฉันไม่รู้ว่านี่เป็นชื่อที่ยอมรับหรือไม่ แต่จาก Smalltalk สมัยเก่าของฉันฉันจำคำว่า "ภาพ" เพื่อระบุสิ่งที่มีการเปลี่ยนแปลงในสิ่งที่สังเกตได้

มันไม่ซับซ้อนเท่ากับความคิดของคุณเกี่ยวกับ EventArg (และการแบ่งคลาสย่อย) มันเพิ่งผ่านสาย (อาจเป็นค่าคงที่จำนวนเต็ม) จากที่สังเกตได้เพื่อสังเกตการณ์

บวก: มีวิธีการง่ายๆสองวิธี ( update(observable)และupdateAspect(observable, aspect)

ลบ: ผู้สังเกตการณ์อาจต้องขอให้สังเกตได้สำหรับข้อมูลเพิ่มเติม (เช่น "จำนวนเต็ม" ของคุณ)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.