簡體   English   中英

觀察者主題單例模式?

[英]Observer Subject Singleton pattern?

我正在設計一種模式,其中將有多個主題和多個觀察者。 每個觀察者都將自己注冊到其構造函數中所需的主題,因為它知道生成輸出所需要知道的內容。 我的第一個想法是使主題成為單例類,但是我發現這要么需要使每個主題分別成為一個單例,要么需要Java不支持的abstract static類。 最重要的是,我試圖找出最好的解決方案是什么。 為了確保它很清楚,這是我要嘗試的操作:

  1. 將有多個主題。 每個觀察者將具有觀察多個主題的能力。 為了使這項工作有效,我將為每個主題准備一個資源代碼,以便它將自己標識給觀察者(`update(SUBJECT_1),其中SUBJECT_1是與主題1對應的int)
  2. 每個觀察者將知道它想要在構造中觀察哪些主題。
  3. 我只希望創建每個主題的一個實例。
  4. 一些觀察者可能反過來成為自己的主體。
  5. 通常,存在一種流模式。 最終會有一些輸入源,這些輸入源會創建更多抽象的概念。 我的觀察者/對象的流程圖如下所示:

在此處輸入圖片說明

我認為這可能會完全需要另一種設計模式,但是由於我對設計模式總體還是比較陌生,所以我想在進入之前需要更多的經驗。謝謝!

需要明確的是,單例的播放方式如下,即我想要一個abstractSubject,其中每個主題都是一個Singleton。 雖然我可以使每個單獨達成一致,但我寧願被迫全部使用一個getInstance,例如這樣的偽代碼。

abstract class abstractSubject{

AbstractSubject instance=null;

public static AbstractSubject getInstance()
{
    if (instance==null)
        instance=new AbstractInstance();
    return instance;
}
abstract void attach();
//More stuff will go here to make it a subject
}

//Define Subject1, Subject2 classes fully

class Observer1 extends AbstractObserver{

    public Observer1()
    {
        Subject1.getInstance().attach(this);
        Subject2.getInstance().attach(this);
    }
    //Other stuff goes here to make it an observer
}

經過一番思考之后,最好的事情似乎是擁有一個看起來像這樣的模式,我認為這與Builder或Factory類有關,但是我仍在嘗試確定其工作方式。

  1. 有一個第三方類,其職責是跟蹤創建的所有主題,並在有要求時提供對它們的引用。 由於缺乏更好的期限,我將其稱為經紀人。
  2. 每個觀察者都可以訪問此代理。 他們向經紀人詢問他們想要什么,如果尚未實例化該對象,它將創建該對象。

如果所有觀察者都被構造為使用此Broker,那么他們只需要知道調用即可使Broker返回他們想要的東西,這應該足夠簡單。 每次創建一個新的Subject時,都需要更新Broker,但是這似乎是我想出的最簡潔的方法來使所有這些工作正常進行。

另外,Broker類包含一個包含所有主題的Enum,因此,它可以充當跟蹤與給定類相對應的Enum值的有效點。

看起來像這樣:

public class Broker {
    public enum Subject {Subj1, Subj2};

    static private Broker instance=null;

    HashMap<Subject,AbstractSubject> mSubjectDict;

    public static Broker getInstance()
    {
        return instance;
    }

    private Broker()
    {
        mSubjectDict=new HashMap<Subject,AbstractSubject>();
        mSubjectDict.put(Subject.Subj1,new Subject1());
        mSubjectDict.put(Subject.Subj2,new Subject2());
    }

    public AbstractLocationSubject getSubject(Subject subj)
    {
        return mSubjectDict.get(subj);
    }
}

添加一個新的可能的Subject所需要做的就是向Enum添加一個值,並在mSubjectDict中創建Subject。 總體而言,我對這種解決方案感到滿意。

我真的不明白,為什么您甚至想到一個Singleton。 它對觀察者模式沒有任何幫助。 不惜一切代價避免使用Singleton,直到您是一個非常優秀的程序員,或者您真的看不到其他任何可行的選擇。

通知一些觀察者和一些不基於硬編碼數字的觀察者的方法實際上是非面向對象的。

例如,您可以創建一個名為“ Quests”的枚舉,每個觀察者將從該枚舉中擁有自己的值列表。 當您從一個主題或多個主題中對觀察者進行注釋時,您只需遍歷已注冊觀察者的整個列表,然后根據該枚舉對它們進行注釋(或者您可以通知所有人,將此枚舉作為notyfing的參數作為消息,您可以讓觀察者決定,如果他們做任何事情)。

編輯(基於添加到您的帖子中的5.):看起來您確實不需要觀察者模式,它通常用作事件處理程序。 如果您的數據從一個實例流向另一個實例,並且您知道其流向等,則實際上並不需要觀察者。 我不知道您對數據到底做了什么,您可以考慮責任鏈。

但是,如果您的實例只是接收消息,對其進行計數,然后再將下一條消息發送給其他實例,那么至少觀察者的想法(注冊和通知)是一個好方法。

您是否要構建工作流引擎?

已經有很多這樣的東西: 我應該使用哪個基於Java的工作流引擎?

我還要將Spring Integration和Spring Batch添加到列表中。

我不能說Java,但是在C ++中遇到了類似的問題。

這是我提出的解決方案的概圖。 它是用C ++編寫的,但是基本思想應該可以很好地轉換為Java(或其他OO語言):

  1. 觀察者是帶有鍵的單例(枚舉值,而不是字符串;這是一種速度折衷,因為不對鍵進行散列搜索,但這意味着沒有簡單的“字符串”名稱,您必須提前定義它們)以便主題注冊感興趣。因為它是一個單例,所以它始終存在。
  2. 每個主題都源自一個共同的基類。 基類有一個抽象的虛函數Notify(...),必須在派生類中實現,以及一個析構函數,將其從Observer中刪除(始終可以到達),然后將其刪除。
  3. 在觀察者內部,如果在進行Notify(...)時調用了Detach(...),則所有分離的Subject最終出現在列表中。
  4. 在觀察者上調用Notify(...)時,它將創建“主題”列表的臨時副本。 在對其進行迭代時,會將其與最近分離的對象進行比較。 如果目標不在其上,則在目標上調用Notify(...)。 否則,它將被跳過。
  5. 觀察器中的Notify(...)還會跟蹤處理級聯調用的深度(A通知B,C,D和D。Notify(...)觸發對E的Notify(...)調用,等等。)

界面最終看起來像這樣:

/* 
 The Notifier is a singleton implementation of the Subject/Observer design
 pattern.  Any class/instance which wishes to participate as an observer
 of an event can derive from the Notified base class and register itself
 with the Notiifer for enumerated events.

 Notifier derived classes MUST implement the notify function, which has 
 a prototype of:

 void Notify(const NOTIFIED_EVENT_TYPE_T& event)

 This is a data object passed from the Notifier class.  The structure 
 passed has a void* in it.  There is no illusion of type safety here 
 and it is the responsibility of the user to ensure it is cast properly.
 In most cases, it will be "NULL".

 Classes derived from Notified do not need to deregister (though it may 
 be a good idea to do so) as the base class destructor will attempt to
 remove itself from the Notifier system automatically.

 The event type is an enumeration and not a string as it is in many 
 "generic" notification systems.  In practical use, this is for a closed
 application where the messages will be known at compile time.  This allows
 us to increase the speed of the delivery by NOT having a 
 dictionary keyed lookup mechanism.  Some loss of generality is implied 
 by this.

 This class/system is NOT thread safe, but could be made so with some
 mutex wrappers.  It is safe to call Attach/Detach as a consequence 
 of calling Notify(...).  

 */


class Notified;

class Notifier : public SingletonDynamic<Notifier>
{
public:
   typedef enum
   {
      NE_MIN = 0,
      NE_DEBUG_BUTTON_PRESSED = NE_MIN,
      NE_DEBUG_LINE_DRAW_ADD_LINE_PIXELS,
      NE_DEBUG_TOGGLE_VISIBILITY,
      NE_DEBUG_MESSAGE,
      NE_RESET_DRAW_CYCLE,
      NE_VIEWPORT_CHANGED,
      NE_MAX,
   } NOTIFIED_EVENT_TYPE_T;

private:
   typedef vector<NOTIFIED_EVENT_TYPE_T> NOTIFIED_EVENT_TYPE_VECTOR_T;

   typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T> NOTIFIED_MAP_T;
   typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T>::iterator NOTIFIED_MAP_ITER_T;

   typedef vector<Notified*> NOTIFIED_VECTOR_T;
   typedef vector<NOTIFIED_VECTOR_T> NOTIFIED_VECTOR_VECTOR_T;

   NOTIFIED_MAP_T _notifiedMap;
   NOTIFIED_VECTOR_VECTOR_T _notifiedVector;
   NOTIFIED_MAP_ITER_T _mapIter;

   // This vector keeps a temporary list of observers that have completely
   // detached since the current "Notify(...)" operation began.  This is
   // to handle the problem where a Notified instance has called Detach(...)
   // because of a Notify(...) call.  The removed instance could be a dead
   // pointer, so don't try to talk to it.
   vector<Notified*> _detached;
   int32 _notifyDepth;

   void RemoveEvent(NOTIFIED_EVENT_TYPE_VECTOR_T& orgEventTypes, NOTIFIED_EVENT_TYPE_T eventType);
   void RemoveNotified(NOTIFIED_VECTOR_T& orgNotified, Notified* observer);

public:

   virtual void Reset();
   virtual bool Init() { Reset(); return true; }
   virtual void Shutdown() { Reset(); }

   void Attach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
   // Detach for a specific event
   void Detach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
   // Detach for ALL events
   void Detach(Notified* observer);

   /* The design of this interface is very specific.  I could 
    * create a class to hold all the event data and then the
    * method would just have take that object.  But then I would
    * have to search for every place in the code that created an
    * object to be used and make sure it updated the passed in
    * object when a member is added to it.  This way, a break
    * occurs at compile time that must be addressed.
    */
   void Notify(NOTIFIED_EVENT_TYPE_T, const void* eventData = NULL);

   /* Used for CPPUnit.  Could create a Mock...maybe...but this seems
    * like it will get the job done with minimal fuss.  For now.
    */
   // Return all events that this object is registered for.
   vector<NOTIFIED_EVENT_TYPE_T> GetEvents(Notified* observer);
   // Return all objects registered for this event.
   vector<Notified*> GetNotified(NOTIFIED_EVENT_TYPE_T event);
};

/* This is the base class for anything that can receive notifications.
 */
class Notified
{
public:
   virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const void* eventData) = 0;
   virtual ~Notified();

};

typedef Notifier::NOTIFIED_EVENT_TYPE_T NOTIFIED_EVENT_TYPE_T;

注意:Notified類具有單個函數Notify(...)。 因為void *不是類型安全的,所以我創建了其他版本的notify看起來像這樣:

virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, int value); 
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const string& str);

相應的Notify(...)方法已添加到通告程序本身。 所有這些都使用單個函數來獲取“目標列表”,然后在目標上調用適當的函數。 這樣效果很好,使接收者不必進行難看的投射。

這似乎運作良好。 該解決方案在網絡上張貼在這里的源代碼一起。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM