如何在任务系统中构建/设计事件和任务以使代码易于管理?

时间:2016-06-21 15:10:46

标签: javascript oop design-patterns event-handling observer-pattern

我正在创建一个Javascript游戏,为用户提供任务。每个任务都有许多任务,在满足某些要求时完成。以下是我原本打算使用的设计。

enter image description here

如您所见,每个TaskGameEvent(简化为Event)都有一个类型和一些数据(封装在一个dict中)。当事件发送到QuestManager时,会将其传递给活动的Quest个对象,而这些对象又将其传递给每个Task,假设它们的类型相同。然后,根据事件中的相关requirement对任务中的每个data进行测试。我不进行1:1检查的原因是因为事件可能有其他数据。

例如data{item: someItem, locationFound: someLocation} != requirement{item: someItem}

这在最小化需要创建的类的数量方面非常有用。事件和任务是通用的,因此可以轻松创建和测试它们,而无需创建大量的子类。

但这种设计会产生问题。由于它的通用性,在没有首先找到相同类型的事件并检查发送给它的数据的情况下,永远不能确定应该给予任务什么要求。随着项目的扩展,这将成为管理的噩梦。

相反,我可以为EventTask创建涵盖各种类型的子类,如下所示:

Event -> ItemPickupEvent, MoveEvent, etc.
Task -> ItemPickupTask, ItemPickedUpAtLocationTask, MoveTask, etc.

但他们之间的数据相似,似乎非常不必要。

我最后的想法是创建数据对象来封装某些事件类型传递的数据,如下所示:

ItemPickupData,MoveData,TalkData等。

可以为事件和任务提供适当的数据对象。这为所使用的数据提供了更好的结构。如果不应将一条数据作为一项要求,则可以简单地将其默认为空。

在可维护性和结构设计方面,这是一个适当的解决方案还是有更好的方法来解决这个问题?

感谢。

1 个答案:

答案 0 :(得分:2)

  

由于它的通用性,如果没有先找到相同类型的事件并检查发送给它的数据,就不能确定应该给任务什么样的要求。

据我所知,问题是事件对象和任务对象中几乎相同的数据都是重复的。

例如,您有Event { 'type': 'ItemPickup', data: { 'name': 'MagicSword', 'kind': 'weapon' } }

然后你想添加一个任务来拾取魔剑并创建Task { 'type': 'ItemPickup', data: { 'name': 'MagicSword' } }。 所以这就是问题,在这里你需要查看你如何配置事件并将相同的数据放入任务对象。

我认为将这些数据移动到单独的对象中的解决方案应该运行良好。 另一件看起来不正确的事情是你有type字段,它暗示你需要有子类。 如果您创建ItemPickupDataMoveData等数据对象,也可以解决此问题。

另一种方法是让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}})。