В данном конкретном случае комментарии объясняют:
/**
* Executes the current command.
*
* This method is not abstract because you can use this class
* as a concrete class. In this case, instead of defining the
* execute() method, you set the code to execute by passing
* a Closure to the setCode() method.
*
* @return int|null null or 0 if everything went fine, or an error code
*
* @throws LogicException When this abstract method is not implemented
*
* @see setCode()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
throw new LogicException('You must override the execute() method in the concrete command class.');
}
Вы можете поспорить с общим дизайном, так как он, возможно, немного хакерский, но он хорошо работает на практике. Взгляните на Command :: run, чтобы увидеть, где принято решение использовать замыкание или выполнить. Немного нишевый случай, если не сказать больше.
Я знаю, что многое из этого обсуждалось в комментариях к другому ответу, но я подумал, что это может помочь в обобщении. Я также провел быстрый поиск по коду фреймворка Symfony, чтобы увидеть, где был использован подход замыкания. Ничего не нашел. Поддержка замыкания полностью восходит к исходному выпуску 2.0, поэтому в нем может быть один из тех «казавшихся в то время хорошей идеей» кусочков функциональности.