我使用命令设计模式来处理玩家操作。
例如,下面是处理掷骰子的命令。
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());
// ...
}
}
谢谢!
DDD 中最常用的命令模式形式(来自 CQRS 的命令模式)与 Go4 命令模式不同。它只是一个没有
execute()
方法的 DTO。
在 DDD 中,应用逻辑位于命令处理程序/应用程序服务中,而不是命令本身。
请注意,您当前在
execute()
中拥有的大部分逻辑是域逻辑,甚至不应该位于命令处理程序中。 Go to jail
、Next player
、Move forward
- 这些看起来像是应该位于域层中的域规则。
我应该直接从滚动命令调用另一个命令还是我 需要避免吗?在命令中抛出事件的想法似乎 对我更好。对此你有何感想?
这取决于您是否认为后续行动是主要行动的一部分还是间接后果。间接命令通常作为单独事务的一部分执行。
当您想要将请求封装为对象时,命令模式非常有用。这允许您在实例化它们时向它们传递参数,将它们分组在一起(将它们作为一个块执行),记录它们,甚至撤消它们。
我(还)没有看到你需要这个的原因。
在命令中存储额外的业务逻辑是个好主意吗?
存储业务逻辑(在表示层中)不好的一个原因是,如果您想添加应用程序的另一个版本(例如移动版本),则必须在新应用程序中重复业务逻辑代码(在其表示层)。此外,维护和测试业务逻辑也更困难,因为它没有真正很好地封装(它是分散的)。
但是,在这里,您已将其中一些封装在 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,因为它会增加复杂性,而没有模式的意图。