$ git diff
src/app/page.tsx
src/components/about.tsx
$ git addsrc/app/page.tsx $
$ git commit -m'feat: add about page'
$ git push
$ git diff
src/app/page.tsx
src/components/about.tsx
$ git addsrc/app/page.tsx $
$ git commit -m'feat: add about page'
$ git push
Para deixar o desafio mais interessante, o código foi limitado a um máximo de uma cláusula if nas regras de negócio.
Mais informações sobre os padrões: Chain of Responsability e Strategy
O padrão Strategy consiste em criar diferentes classes que juntas formam um algoritmo, mas cada uma pode ser separada com certa facilidade e trocada por outra equivalente sem quebrar o fluxo do código, mas adicionando efeitos colateiras ou alterando o resultado.
Um arquivo principal define a estratégia, e pega cada um desses aqruivos "handlers" para adicionar no seu algoritmo.
Temos aqui a classe Counter.php:
Nessa classe, utilizando a classe Rule, conseguimos definir uma estratégia para o algoritmo
<?php
namespace FizzBuzz\Domain;
use FizzBuzz\Infra\Rule;
class Counter
{
private $ruleChain; // A estratégia (ou a cabeça da cadeia de responsabilidade)
private $position;
public function __construct(Rule $ruleChain) // Inversão de Dependência (DIP)
{
$this->position = 0;
$this->ruleChain = $ruleChain;
}
public function count() // O método principal que utiliza a estratégia
{
$message = $this->ruleChain->replace($this->position); // Delegação para a estratégia/cadeia
++$this->position;
return $message;
}
public function reset()
{
$this->position = 0;
}
}
A classe Counter não precisa necessariamente saber o que está acontecendo dentro de Rule, contanto que a classe que estiver implementando a interface implemente-a de forma correta, seguindo o contrato da interface.
Dessa forma, qualquer arquivo derivado de Rule pode ser usado como a "estratégia" de execução do Counter.
O padrão CoR consiste em criar diferentes classes que atuarão em cima do request na ordem que foi definida. As classes podem ser chamadas handlers, e cada uma decide se vai agir em cima do objeto ou passar para a próxima da cadeia.
As definições sobre os princípios SOLID são apenas rascunhos sobre meu entendimento delas.
A classe handler seria a classe Rule:
<?php
namespace FizzBuzz\Infra;
interface Rule
{
public function replace(int $value): string;
}
À partir dessa interace, podemos criar diferentes regras que fazer um "replace" de um valor, recebendo um parâmetro e retornando outro baseado em alguma regra.
Para o problema em questão, FizzBuzz, implementei uma regra chamada ModRule, que faz o mod de um número (retornando o restante da divisão).
use FizzBuzz\Infra\Rule;
class ModRule implements Rule
{
protected $number;
protected $message;
protected $nextRule;
public function __construct(int $number, string $message, Rule $nextRule = null)
{
$this->number = $number;
$this->message = $message;
$this->nextRule = $nextRule;
}
public function replace(int $value) : string
{
$message = (string) $value; // Valor padrão se nenhuma regra for aplicada
if (isset($this->nextRule)) { // Se houver uma próxima regra, passa a responsabilidade
$message = $this->nextRule->replace($value);
}
// A "única" instrução if relevante para a regra específica
if ($message === (string) $value AND
$value !== 0 AND
$value % $this->number === 0)
{
$message = $this->message;
}
return $message;
}
}
ifAqui na classe ModRule vemos a implementação principal da regra de negócio que será usada como o padrão Strategy para resolução do desafio.
Como é possível ver, ela possui doi s ifs... Mas, parando para analisar, um deles é apenas para garantir que existe uma próxima regra definida, o que é algo relacionado ao design de Chain of Responsability. E tirando esse, sobra o único if que realmente impacta na regra de negócio:
if ($message === (string) $value AND
$value !== 0 AND
$value % $this->number === 0)
{
$message = $this->message;
}