观察者主题单身人士模式?

时间:2013-10-16 01:12:52

标签: java design-patterns singleton observer-pattern

我正在设计一个模式,其中将有多个主题和多个观察者。每个观察者都会在其构造函数中注册它所需要的主题,因为它知道生成它的输出需要知道什么。我的第一个想法是使主题成为单例类,但我发现要么要求每个主题单独使用单个,要么使Java不支持的abstract static类。最重要的是,我正在试图找出最佳解决方案的真正含义。只是为了确保它非常清楚,这就是我要做的事情:

  1. 会有多个科目。每个观察者都有能力观察多个科目。为了使这项工作,我将为每个主题提供一个资源代码,以便它将自己标识给Observer(`update(SUBJECT_1),其中SUBJECT_1是与Subject 1对应的int)
  2. 每个观察者都会知道在建筑时要观察哪些主题。
  3. 我只想要创建每个主题的单个实例。
  4. 有些观察者可能反过来成为自己的主体。
  5. 通常,存在流动模式。最终有一些输入源,它们创建了更抽象的概念。我的观察者/主题的流程图看起来像这样:
  6. enter image description here

    我认为这可能会完全要求另一种设计模式,但由于我在设计模式方面仍然相当新,我认为在我跳入之前我会要求更多的经验。谢谢!

    为了清楚起见,单身人士按照以下方式进行游戏,即我想要一个abstractSubject,其中每个主题都是一个单身人士。虽然我可以使每个单独的单独使用,但我宁愿被迫使用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
    }
    

4 个答案:

答案 0 :(得分:1)

经过一番思考之后,最好的事情似乎是拥有一个看起来像这样的模式,我认为这与Builder或Factory类有关,但我还是想弄清楚它是怎么回事的工作原理。

  1. 有一个第三方课程,其工作是跟踪所有创建的主题,并在被询问时提供对它们的引用。由于缺乏更好的术语,我将其称为经纪人。
  2. 每位观察者都可以访问此经纪人。他们向经纪人询问他们想要什么,如果尚未实例化,它会创建对象。
  3. 如果构造所有观察者都使用这个Broker,那么他们只需要知道调用以让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);
        }
    }
    

    所有添加新的可能主题要求是向枚举添加值,并在mSubjectDict中创建主题。总的来说,我对这个解决方案感到满意。

答案 1 :(得分:0)

我真的不明白,为什么你甚至想到一个单身人士。它对观察者模式中的任何东西都没有帮助。在你是一个非常优秀的程序员或你真的没有看到任何其他可重复的选择之前,不惜一切代价避免使用Singleton。

让一些观察者感到高兴的方法和一些不基于硬编码的方法实际上是非OOP。

例如,您可以创建名为“Quests”的枚举,每个观察者将拥有自己的枚举值列表。当你不是来自主题或主题的观察者时,你只需要浏览注册观察者的整个列表并根据该枚举来理解它们(或者你可以通知所有人,将这个enum作为notyfing的参数作为一个消息,你可以让观察者决定,如果他们做了什么)。

编辑(根据你的帖子添加5.):它看起来不像你真的需要Observer模式,它通常用作事件的处理程序。如果您的数据从一个实例流向另一个实例,并且您知道它将如何流动等,则实际上并不需要Observer。我不知道你究竟在做什么数据,你可以考虑责任链。

但是,如果你的实例只收到消息,计算它们,然后将下一条消息发送到其他实例,至少观察者的想法 - 注册和notyfing - 是很好的方法。

答案 2 :(得分:0)

您是否正在尝试构建工作流引擎?

那里已经有很多人: Which Java based workflow engine should I use?

我还要将Spring Integration和Spring Batch添加到列表中。

答案 3 :(得分:0)

我不能谈论Java,但我在C ++中遇到过类似的问题。

以下是我提出的解决方案的粗略草图。它是用C ++编写的,但基本思想应该很好地转换为Java(或其他OO语言):

  1. Observer是一个带键的单例(枚举值,而不是字符串;这是一个速度权衡,因为键没有被搜索哈希,但它意味着没有简单的“字符串”名称,你必须提前定义它们)注册兴趣的主题。因为它是一个单身人士,所以它总是存在。
  2. 每个主题都来自一个共同的基类。基类有一个抽象的虚函数Notify(...),它必须在派生类中实现,还有一个析构函数,当它被删除时,它会从Observer中删除它(它总是可以到达)。
  3. 在Observer本身内部,如果在Notify(...)正在进行时调用Detach(...),任何分离的Subjects都会在列表中结束。
  4. 在Observer上调用Notify(...)时,它会创建Subject列表的临时副本。当它迭代它时,它将它与最近分离的它进行比较。如果目标不在其上,则在目标上调用Notify(...)。否则,它会被跳过。
  5. 观察者中的通知(...)还跟踪处理级联呼叫的深度(A通知B,C,D和D.Notify(...)触发Notify(...)呼叫到E等。)
  6. 接口最终看起来像这样:

    /* 
     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(...)方法被添加到Notifier本身。所有这些都使用单个函数来获取“目标列表”,然后在目标上调用适当的函数。这很好用,并使接收器不必做丑陋的演员。

    这似乎运作良好。该解决方案与源代码一起发布在网络here上。