命令设计模式中的业务逻辑

3
我使用命令设计模式来处理玩家的操作。
例如,下面是处理掷骰子的命令。
interface ICommand
{
    public function execute(Game $game) : void;
}

class RollDiceCommand implements ICommand
{
    private $player;

    public function __construct(Player $player)
    {
        $this->player = $player;
    }

    public function execute(Game $game) : void
    {
        $dice = DiceFacade::roll(new NumberGenerator());

        // Currently a business logic goes here

        if ($dice->isDouble()) {
            $player->incrementDoubleCount();

            if ($player->getDoubleCount() === 3) {
                $command = new GoToJailCommand();
                $command->execute();

                return;
            }
        } else {
            // The next player turn
            $game->nextPlayer();
        }

        $command = MoveForwardCommand($this->player);
        $command->execute($dice->getValue());

        // ...
    }
}
  1. 在命令中存储额外的业务逻辑是个好主意吗?
  2. 我应该直接从roll命令调用另一个命令,还是需要避免这样做?在命令中抛出事件的想法对我来说似乎更好。您对此有什么看法?

谢谢!


GoF书中关于命令模式的定义是:“Intent 将请求封装为对象,从而可以使用不同的请求参数化客户端,排队或记录请求并支持可撤消的操作。”这就是为什么你在这里使用命令模式的原因吗? - Fuhrmanator
2个回答

3
在领域驱动设计(DDD)中,最常使用的Command模式形式(来自CQRS)与Go4 Command模式并不相同。它只是一个没有execute()方法的DTO。
在DDD中,应用逻辑位于命令处理程序/应用程序服务中,而不是命令本身。
请注意,您当前在execute()中具有的大部分逻辑都是领域逻辑,甚至不应该在命令处理程序中。"去监狱","下一位玩家","前进" - 这些看起来像应该在Domain层中的领域规则。

我应该直接从roll命令调用另一个命令还是需要避免?在命令中抛出事件的想法对我来说更好。你对此有什么看法?

这取决于您是否认为随后的移动是主要操作的一部分还是间接后果。间接命令通常作为单独事务的一部分执行。

0

命令模式在想要将请求封装为对象时非常有用。这样可以在实例化时向它们传递参数,将它们分组在一起(作为块执行),记录它们,甚至撤消它们。

我还没有看到你需要这个的原因。

在Command中存储附加的业务逻辑是否是一个好主意?

将业务逻辑存储在表示层中是不好的一点就是,如果您想要添加应用程序的另一个版本(例如移动版本),则必须在新应用程序中(在其表示层中)重复业务逻辑代码。此外,由于业务逻辑并没有很好地封装起来(它是分散的),因此更难以维护和测试。

但是,在这里,您已经将其中一些封装在了Command对象中,这可能不是很糟糕(取决于您在哪里看到这段代码)。对于大富翁游戏,您是否会有多个客户端(不同的表示层?)还是这是一个宠物项目(一个实现)?如果将有不同的表示层,则最好将域逻辑排除在外。在您的示例代码中(但我不太擅长PHP),Command中没有看起来太与表示层相关的内容,因此可能是可以的。

通常,如果您正在尝试封装领域逻辑,则GoF Façade模式很有用。您将在领域层中拥有一个Façade类,该类处理高级操作(例如rollAndMove($dice))。看起来您已经为掷骰子使用了Façade。Player也可以是扮演Façade角色的类,因为采取回合的领域逻辑对于该类来说是一个合理的责任(在我看来)。当然,如果Player最终拥有太多方法,最好使用单独的类作为Façade。
引发事件的想法对我来说更好。你怎么看?
我认为结合两种模式没有问题,但也许您不需要Command的本意?您说得对,execute()代码非常简短(只需调用Facade的方法)。但是,使用Command对象允许您保存游戏状态(例如GoF Memento模式),以便稍后撤消命令,或者如上所述,您可以以标准方式记录信息等。如果您不需要这些功能,则应避免使用Command,因为它会增加复杂性而没有模式的意图。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接