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

问题描述 投票:0回答:2

我使用命令设计模式来处理玩家操作。

例如,下面是处理掷骰子的命令。

    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. 我应该直接从滚动命令调用另一个命令还是需要避免它?在命令中抛出事件的想法对我来说似乎更好。对此你有何感想?

谢谢!

oop design-patterns domain-driven-design
2个回答
3
投票

DDD 中最常用的命令模式形式(来自 CQRS 的命令模式)与 Go4 命令模式不同。它只是一个没有

execute()
方法的 DTO。

在 DDD 中,应用逻辑位于命令处理程序/应用程序服务中,而不是命令本身。

请注意,您当前在

execute()
中拥有的大部分逻辑是域逻辑,甚至不应该位于命令处理程序中。
Go to jail
Next player
Move forward
- 这些看起来像是应该位于域层中的域规则。

我应该直接从滚动命令调用另一个命令还是我 需要避免吗?在命令中抛出事件的想法似乎 对我更好。对此你有何感想?

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


0
投票

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

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

在命令中存储额外的业务逻辑是个好主意吗?

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

但是,在这里,您已将其中一些封装在 Command 对象中,这可能还不错(取决于您在“何处”看到此代码)。对于大富翁游戏,您是否有多个客户端(不同的表示层?)或者这是一个宠物项目(一个实现)?如果存在不同的表示层,那么最好将领域逻辑排除在外。您的示例代码中没有任何命令(但我不擅长 PHP)看起来与演示文稿太相关,所以可能没问题。

通常,如果您尝试封装领域逻辑,GoF Façade 模式会很有用。您将在域层中有一个 Façade 类来处理高级操作(例如,

rollAndMove($dice)
)。看来您已经使用 Façade 来掷骰子了。 Player 也可以是扮演 Façade 角色的类,因为轮流的域逻辑是该类的合理责任(IMO)。当然,如果 Player 最终有太多方法,那么为 Façade 使用单独的类可能会更好。

在命令中抛出事件的想法对我来说似乎更好。你对此有何看法?

我认为组合这两种模式没有问题,但也许您并不真正需要 Command 来实现它的目的?

你说得对,

execute()
将是非常短的代码(只需调用Facade的方法)。但是,如果您想稍后撤消命令,则使用 Command 对象可以让您保存游戏状态(例如,GoF Memento 模式),或者如上所述,您可以以标准方式记录信息等。如果您不这样做不需要这些东西,我会避免使用 Command,因为它会增加复杂性,而没有模式的意图。

© www.soinside.com 2019 - 2024. All rights reserved.