gpt4 book ai didi

c++ - 符号属性观察c++

转载 作者:行者123 更新时间:2023-11-28 07:24:23 25 4
gpt4 key购买 nike

我有以下问题。考虑方案: enter image description here

在绘图类中我有:

Box* b1 = new Box();
Box* b2 = new Box();
Box* b3 = new Box();
Box* b4 = new Box();

b1->setArea(20);
b2->setArea(30);
b3->setArea( b1->getArea() * 7/3 + b2->getArea() / 5 );
b4->setArea( b3->getArea() + b1->getArea() );

我想要系统的以下行为:

  • 如果我更改 b1 的面积(即 b1->setArea(25) ),我希望其他框根据上述表达式相应地改变面积(即我希望 b2 保持其面积,但 b3 和 b4 改变它们的面积区域为 b1->getArea()*7/3 + b2->getArea()/5 和 b4 分别为 b3->getArea() + b1->getArea())。这意味着 b1 区域的更新会自动更新 b3 和 b4 的区域。

我想知道我应该如何使用 C++ 正确实现此功能?

我看了观察者模式,但似乎不适用于这种情况,这是正确的吗?

谢谢

最佳答案

似乎如果“盒子”正在观察系统中的单个“事件”,例如以“b1”作为参数的“BOX_AREA_CHANGED”,它们都可以根据需要更改自己的区域。

所以这确实感觉像是一种“观察者”模式。问题在于让所有参与者(盒子)在创建/销毁时根据需要附加/分离。此外,您还希望他们详细注册特定事件,以便您可以轻松地针对他们的操作。如果所有框都响应“BOX_AREA_CHANGED”,但只有少数框响应“BOX_MOVED”,您不希望前者在后一条消息发生变化时做出响应。

我把类似这样的东西放在一起,叫做通知程序。这是它的基本要点:

  1. Observer 是一个带有键(枚举值,而不是字符串)的单例,供 Subjects 注册兴趣。因为它是单例,所以它始终存在。
  2. 每个主题都派生自一个公共(public)基类。基类有一个必须在派生类中实现的抽象虚函数 Notify(...),以及一个在删除时将其从 Observer(它始终可以到达)中移除的析构函数。
  3. 在 Observer 内部,如果在 Notify(...) 进行时调用 Detach(...),任何分离的 Subjects 最终都会出现在列表中。
  4. 当在 Observer 上调用 Notify(...) 时,它会创建 Subject 列表的临时拷贝。当它遍历它时,它将它与最近分离的进行比较。如果目标不在其上,则在目标上调用 Notify(...)。否则,它会被跳过。
  5. Observer 中的 Notify(...) 还跟踪处理级联调用的深度(A 通知 B、C、D,D.Notify(...) 触发 Notify(...) 调用到 E 等)

界面最终是这样的:

/* 
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 Notifier for enumerated events.

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

void Notify(const NOTIFIED_EVENT_TYPE_T& event, void* data)

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(...) 方法已添加到通知程序本身。所有这些都使用一个函数来获取“目标列表”,然后在目标上调用适当的函数。这很好用,并且让接收者不必做丑陋的转换。

这似乎运作良好。解决方法贴在网上here连同源代码。这是一个相对较新的设计,因此非常感谢任何反馈。

关于c++ - 符号属性观察c++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18984729/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com