oopdesign-patternsdomain-driven-design

Business logic in command design pattern


I use the command design pattern to deal with player actions.

For example, below is the command that handles a dice roll.

    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. Is it good idea to store an additional business logic in the command?
  2. Should I call an another command directly from the roll command or I need to avoid it? The idea of throwing an event in the command seems better to me. What do you think about it?

Thank you!


Solution

  • The Command pattern is useful when you want to encapsulate requests as an object. That allows you to pass parameters to them when they're instantiated, to group them together (executing them as a block), to log them, and even undo them.

    I'm not seeing (yet) a reason you need this.

    Is it good idea to store an additional business logic in the command?

    One reason it's bad to store business logic (in the presentation layer) is that if you want to add another version of your application (say, a mobile version), you have to repeat the business logic code in the new application (in its presentation layer). Also, it's harder to maintain and test the business logic, because it's not really very well encapsulated (it's spread out).

    Here, however, you've encapsulated some of it in a Command object, which may not be bad (depending on "where" you see this code). For the game of Monopoly, will you have multiple clients (different presentation layers?) or is this a pet project (one implementation)? If there are going to be different presentation layers, then it's best to keep the domain logic out of them. There's nothing in your sample code (but I'm not good with PHP) with Command that looks too tied to presentation, so it's probably OK.

    Generally, if you're trying to encapsulate domain logic, the GoF Façade pattern is useful. You'd have a Façade class in the domain layer that handles the high-level operations (e.g., rollAndMove($dice)). It seems you already use a Façade for the dice roll. Player could alternatively be a class that plays the roll of the Façade, since the domain logic of taking a turn would be a reasonable responsibility for that class (IMO). Of course, if Player ends up with too many methods, it's perhaps better to use a separate class for the Façade.

    The idea of throwing an event in the command seems better to me. What do you think about it?

    I don't see a problem with combining both patterns, but perhaps you don't really need Command for what it's intended to be?

    You're right that the execute() would be very short code (just call the Facade's method). However, using a Command object allows you to save the state of the game (e.g., GoF Memento pattern) if you wanted to undo the command later, or as stated above you could log the information in a standard way, etc. If you don't need those things, I would avoid using Command as it's adding complexity without the intent of the pattern.