我正在创建一个Javascript游戏,为用户提供任务。每个任务都有许多任务,在满足某些要求时完成。以下是我原本打算使用的设计。
如您所见,每个Task
和GameEvent
(简化为Event
)都有一个类型和一些数据(封装在一个dict中)。当事件发送到QuestManager
时,会将其传递给活动的Quest
个对象,而这些对象又将其传递给每个Task
,假设它们的类型相同。然后,根据事件中的相关requirement
对任务中的每个data
进行测试。我不进行1:1检查的原因是因为事件可能有其他数据。
例如data{item: someItem, locationFound: someLocation} != requirement{item: someItem}
这在最小化需要创建的类的数量方面非常有用。事件和任务是通用的,因此可以轻松创建和测试它们,而无需创建大量的子类。
但这种设计会产生问题。由于它的通用性,在没有首先找到相同类型的事件并检查发送给它的数据的情况下,永远不能确定应该给予任务什么要求。随着项目的扩展,这将成为管理的噩梦。
相反,我可以为Event
和Task
创建涵盖各种类型的子类,如下所示:
Event -> ItemPickupEvent, MoveEvent, etc.
Task -> ItemPickupTask, ItemPickedUpAtLocationTask, MoveTask, etc.
但他们之间的数据相似,似乎非常不必要。
我最后的想法是创建数据对象来封装某些事件类型传递的数据,如下所示:
ItemPickupData,MoveData,TalkData等。
可以为事件和任务提供适当的数据对象。这为所使用的数据提供了更好的结构。如果不应将一条数据作为一项要求,则可以简单地将其默认为空。
在可维护性和结构设计方面,这是一个适当的解决方案还是有更好的方法来解决这个问题?
感谢。
答案 0 :(得分:2)
由于它的通用性,如果没有先找到相同类型的事件并检查发送给它的数据,就不能确定应该给任务什么样的要求。
据我所知,问题是事件对象和任务对象中几乎相同的数据都是重复的。
例如,您有Event { 'type': 'ItemPickup', data: { 'name': 'MagicSword', 'kind': 'weapon' } }
。
然后你想添加一个任务来拾取魔剑并创建Task { 'type': 'ItemPickup', data: { 'name': 'MagicSword' } }
。
所以这就是问题,在这里你需要查看你如何配置事件并将相同的数据放入任务对象。
我认为将这些数据移动到单独的对象中的解决方案应该运行良好。
另一件看起来不正确的事情是你有type
字段,它暗示你需要有子类。
如果您创建ItemPickupData
,MoveData
等数据对象,也可以解决此问题。
另一种方法是让Event
本身成为这个数据对象并具有类似的东西(伪代码):
class Event // base class for events with common data and methods
// the subclasses can be almost empty
class ItemPickupEvent extends Event
class MoveEvent extends Event
class Task
_requirements = [
new ItemPickupEvent('MagicSword'),
// and maybe it is even better to wrap events into "Requirment" objects,
// which can, for example, be marked as completed
new Requirement(new MoveEvent('OldForest')),
...
]
然后您可以通过这种方式管理需求:
class Task
isComplete(event)
complete = false
// go over requirements to see if this task is complete
for requirement in this._requirments:
complete = complete && requirement->isComplete(event)
return complete
class Requirement
isComplete(event)
# check event class and properties to understand if the requirement is satisfied
if class(event) == class(this._event):
if event.isNameMatches(this._event):
this->_complete = true
return this->_complete
也可以在没有类名检查的情况下完成。
我们的想法是让基类Requirement
类为每个事件类都有特殊的方法,并默认拒绝它们。
然后子类可以处理特定的事件类型:
class Requirement
isComplete(event)
// redirect the type detection to event object
return event->isComplete(this)
isCompleteItemPickup(event)
return false
isCompleteMove(event)
return false
class ItemPickupRequirement
isCompleteItemPickup(event)
// here we know that event is ItemPickupEvent
if event.isNameMatches(this._event):
this->_complete = true
return this->_complete
class MoveRequirement
isCompleteItemPickup(event)
// here we know that event is MoveEvent
if event.isNameMatches(this._event):
this->_complete = true
return this->_complete
isComplete
类的Requirment
方法用于Task
类,它将类型检测重定向到事件对象。
然后,事件对象将自身返回到需求的适当方法:
class Event
isComplete(requirement) // should be implemented in subclasses
class ItemPickupEvent
isComplete(requirement)
// here we call the specific requirement method
// now we know the exact event object type (it is ItemPickupEvent)
return requirement->isCompleteItemPickup(this)
class MoveEvent
isComplete(requirement)
return requirement->isCompleteMove(this)
结构可能看起来有点复杂,但根据具体情况,您可能还需要Requirement
类及其子类(例如,可以存在泛型ItemPickupRequirement
和某些特定{ {1}})。