Mẫu quan sát; biết * cái gì * đã thay đổi?


10

Tôi đã tạo hai lớp trừu tượng Chủ đề và Người quan sát xác định giao diện mẫu Quan sát cổ điển. Tôi xuất phát từ chúng để thực hiện mẫu Observer. Một người quan sát có thể trông như thế này:

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

Điều này là tốt và nó cho tôi biết ai đã thay đổi một cái gì đó. Tuy nhiên, nó không cho tôi biết điều gì đã thay đổi. Đôi khi điều này là ổn vì tôi sẽ truy vấn Chủ đề cho dữ liệu mới nhất, nhưng lần khác tôi cần biết chính xác những gì đã thay đổi trên Chủ đề. Tôi nhận thấy trong Java họ có cả phương thức notifyObservers () và phương thức notifyObservers (Object arg) để chỉ định chi tiết về những gì đã thay đổi.

Trong trường hợp của tôi, tôi cần biết liệu một trong hai hành động khác nhau có xảy ra với chủ đề hay không, và nếu đó là một hành động cụ thể, để biết một số nguyên liên quan đến hành động đó.

Vì vậy, câu hỏi của tôi là:

  1. Cách C ++ để vượt qua một đối số chung (như Java) là gì?
  2. Observer thậm chí là mô hình tốt nhất? Có lẽ một số loại hệ thống sự kiện?

CẬP NHẬT

Tôi đã tìm thấy bài viết này nói về việc tạo khuôn mẫu cho Người quan sát: Triển khai mẫu Chủ đề / Người quan sát bằng các mẫu . Điều này làm tôi tự hỏi nếu bạn có thể tạo mẫu một đối số.

Tôi đã tìm thấy câu hỏi tràn ngăn xếp này nói về việc tạo khuôn mẫu cho đối số: Mẫu Trình quan sát chủ đề dựa trên mẫu - Tôi nên sử dụng static_cast hoặc Dynamic_cast . Tuy nhiên, OP dường như có một vấn đề mà chưa ai trả lời.

Một điều khác tôi có thể làm là thay đổi phương thức Cập nhật để lấy một đối tượng EventArg như trong:

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

Và sau đó tạo các lớp con của EventArg cho dữ liệu đối số cụ thể, và sau đó tôi đoán chuyển nó trở lại lớp con cụ thể trong phương thức cập nhật.

CẬP NHẬT 2

Cũng tìm thấy một bài viết, Về việc tạo một khung c ++ dựa trên thông điệp không đồng bộ; phần 2 thảo luận về việc Chủ đề truyền đạt chi tiết về những gì đã thay đổi.

Bây giờ tôi nghiêm túc xem xét việc sử dụng Boost.Signals . Sử dụng mẫu quan sát riêng của tôi có ý nghĩa khi nó đơn giản, nhưng tạo khuôn mẫu cho kiểu và một đối số đang bắt đầu trở nên phức tạp. Và tôi có thể cần sự an toàn của luồng Boost.Signals2.

CẬP NHẬT 3

Tôi cũng tìm thấy một số bài viết thú vị về mẫu người quan sát:

Tổng quát hóa Người quan sát bằng Herb Sutter

Triển khai mẫu Người quan sát trong C ++ - Phần 1

Kinh nghiệm thực hiện mẫu thiết kế quan sát viên (Phần 2)

Kinh nghiệm thực hiện mẫu thiết kế quan sát viên (Phần 3)

Tuy nhiên, tôi đã chuyển việc triển khai của mình sang sử dụng Boost.Signals, trong khi có thể được thực hiện cho mục đích của tôi, đang hoạt động thành công. Và có lẽ bất kỳ mối quan tâm về sự phình to hoặc tốc độ là không liên quan.


Các câu hỏi trong cơ thể dường như không thực sự phù hợp với tiêu đề của bạn. Đó thực sự là cốt lõi của câu hỏi?
Nicole

@Renesis: Tôi hiện đang sử dụng mẫu Observer như trong mẫu mã ở đầu bài viết của mình. Đối với mã tôi hiện đang làm việc, hóa ra tôi cần biết cụ thể những gì đã thay đổi để tôi có thể phản ứng tương ứng. Việc triển khai mẫu quan sát hiện tại của tôi (là mẫu chuẩn) không cung cấp thông tin này. Câu hỏi của tôi là làm thế nào để có được thông tin tốt nhất về những gì đã thay đổi.
Người dùng

@Renesis: Đây là một chủ đề diễn đàn đặt câu hỏi tương tự với tôi: gamedev.net/topic/497105-observer-potype
Người dùng

Cập nhật quảng cáo2: Tôi chắc chắn hỗ trợ sử dụng Boost.Signals. Nó thuận tiện hơn nhiều so với việc tự lăn. Ngoài ra còn có libsigc ++ nếu bạn muốn thứ gì đó nhẹ hơn chỉ dành cho tác vụ này (Boost.Signals sử dụng rất nhiều mã từ phần còn lại của Boost); nó không phải là chủ đề an toàn mặc dù.
Jan Hudec

Về các vấn đề tốc độ: Tôi không biết cụ thể Boost Boost nhanh như thế nào, nhưng đó chỉ là vấn đề đáng lo ngại khi bạn có một lượng lớn sự kiện bay xung quanh ...
Tối đa

Câu trả lời:


4

Dù là C ++ hay JAVA, Thông báo cho người quan sát có thể đi kèm với thông tin về những gì được thay đổi. Các phương thức tương tự notifyObservers (Object arg) cũng có thể được sử dụng trong C ++.

Nói chung, vấn đề sẽ vẫn là có thể có nhiều đối tượng gửi đến một hoặc nhiều người quan sát và do đó, class argkhông thể mã hóa cứng.

Thông thường, cách tốt nhất để làm là tạo arg dưới dạng thông báo / mã thông báo chung tạo thành cùng loại dữ liệu cho các lớp khác nhau nhưng giá trị khác nhau cho các lớp được quan sát khác nhau. Ngoài ra, nếu tất cả các giá trị thông báo như vậy được lấy ra khỏi lớp trên một số lớp dựa trên chung cho tất cả.

Đối với mô hình quan sát, nó quan trọng mà Arg kiểu dữ liệu không được mã hóa cứng giữa observee và quan sát viên - nếu không nó là một khớp nối mà làm cho những điều khó khăn để phát triển.

EDIT
Nếu bạn muốn người quan sát đó không chỉ quan sát mà còn cần thực hiện nhiều nhiệm vụ khác dựa trên những gì đã thay đổi sau đó, bạn cũng có thể kiểm tra mẫu Khách truy cập . Trong mẫu khách truy cập, người quan sát / khách truy cập gọi đối tượng đã sửa đổi và do đó không chỉ có thể biết sửa đổi là gì mà còn thực sự có thể hoạt động trên nó


5
Nếu người quan sát giải thích lý luận, có một khớp nối, bất kể thế nào bạn ẩn các loại. Trong thực tế, tôi muốn nói đó là nhiều khó khăn để phát triển nếu bạn vượt qua Object( void *, boost::anyhoặc một cái gì đó chung chung tương tự) hơn nếu bạn vượt qua loại hình cụ thể, bởi vì với các loại cụ thể mà bạn sẽ thấy ở thời gian biên dịch có điều gì thay đổi, trong khi với các loại generic nó sẽ biên dịch và ngừng hoạt động, bởi vì người quan sát sẽ không thể làm việc với dữ liệu thực tế được truyền.
Jan Hudec

@JanHudec: Tôi đồng ý với điều đó, nhưng điều đó có nghĩa là bạn tạo một lớp con Observer / Chủ đề một lần cụ thể cho từng đối số (nghĩa là cho từng trường hợp sử dụng)?
Người dùng

@JanHudec: cũng là khớp nối chỉ có một cách. Đối tượng không có ý tưởng về các nhà quan sát. Vâng, người quan sát biết về chủ đề này nhưng đó không phải là cách mà người quan sát làm việc sao?
Người dùng

1
@ Người dùng: Có, tôi tạo một giao diện cụ thể cho từng đối tượng và mỗi người quan sát thực hiện các giao diện của các đối tượng cần quan sát. Chà, tất cả các ngôn ngữ tôi sử dụng đều có con trỏ phương thức ràng buộc trong ngôn ngữ hoặc khung (đại biểu C #, C ++ 11 std::functionBoost boost::function, Gtk + GClosure, phương thức ràng buộc python, v.v.), vì vậy tôi chỉ xác định phương thức có chữ ký phù hợp và yêu cầu hệ thống tạo thực tế người quan sát. Khớp nối thực sự chỉ là một cách, chủ thể xác định giao diện cho người quan sát, nhưng không có ý tưởng về việc thực hiện chúng.
Jan Hudec

0

Có một vài cách để gửi một đối số sự kiện chung "Giống như Java" trong C ++.

1) Khai báo đối số sự kiện là void * và chuyển nó sang đúng lớp trong trình xử lý sự kiện.

2) Khai báo đối số sự kiện dưới dạng con trỏ / ref cho một lớp / giao diện mới, chẳng hạn như (để đáp lại ví dụ của bạn)

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

Và có các lớp đối số sự kiện thực tế xuất phát từ lớp này.

Đối với bạn câu hỏi liên quan đến một người quan sát dựa trên mẫu kiểm tra

http://www.codeproject.com/Articles/3267/Imcellenceing-a-Subject-Observer-potype-with-templ


-1

Tôi không biết liệu đây có phải là tên chính tắc hay không, nhưng từ những ngày ở Smalltalk cũ của tôi, tôi nhớ thuật ngữ "khía cạnh" để xác định những gì đã thay đổi trên quan sát được.

Nó không phức tạp như ý tưởng của bạn về EventArg (và phân lớp nó); nó chỉ truyền một chuỗi (thậm chí có thể là hằng số nguyên) từ quan sát đến người quan sát.

Thêm vào đó, chỉ có hai phương pháp đơn giản ( update(observable)updateAspect(observable, aspect)

Điểm trừ: Người quan sát có thể phải hỏi người quan sát để biết thêm thông tin (nghĩa là "số nguyên" của bạn)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.