Risolvere il problema FizzBuzz
Il FizzBuzz è un test semplice e comune nei colloqui di lavoro per sviluppatori, progettato per valutare la capacità di scrivere codice pulito e di implementare logica condizionale di base.
Descrizione del problema:
Il test chiede di stampare i numeri da 1 a un limite definito (solitamente 100), con le seguenti regole:
- Se un numero è divisibile per 3, stampare “Fizz” al posto del numero.
- Se un numero è divisibile per 5, stampare “Buzz” al posto del numero.
- Se un numero è divisibile sia per 3 che per 5, stampare “FizzBuzz”.
- Per tutti gli altri numeri, stampare il numero stesso.
A cosa serve?
Il FizzBuzz non è complesso, ma permette ai reclutatori di valutare la capacità del candidato di:
- Gestire la logica condizionale.
- Lavorare con cicli e operazioni base su numeri.
- Scrivere codice chiaro, leggibile e senza errori di sintassi.
- Dimostrare velocemente le competenze di base in un linguaggio di programmazione.
Esempio di output:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
Il problema FizzBuzz può essere risolto in diversi modi, sia utilizzando una logica di base che applicando diversi design patterns. Vediamo un po’ di soluzioni.
Soluzione Base
Questa è la soluzione di base, in cui si utilizza un semplice controllo condizionale:
function fizzBuzz($n) {
for ($i = 1; $i <= 100; $i++) {
if ($i % 3 == 0 && $i % 5 == 0) {
echo "FizzBuzz\n";
} elseif ($i % 3 == 0) {
echo "Fizz\n";
} elseif ($i % 5 == 0) {
echo "Buzz\n";
} else {
echo "$i\n";
}
}
}
Soluzione con lo Strategy Pattern
Qui possiamo usare il Strategy Pattern per gestire le diverse condizioni di Fizz, Buzz e FizzBuzz. Ogni strategia definisce il comportamento da applicare a numeri specifici.
// Interfaccia per le strategie
interface FizzBuzzStrategy {
public function apply($number);
}
// Strategia per FizzBuzz
class FizzBuzz implements FizzBuzzStrategy {
public function apply($number) {
if ($number % 3 == 0 && $number % 5 == 0) {
return "FizzBuzz";
}
return null;
}
}
// Strategia per Fizz
class Fizz implements FizzBuzzStrategy {
public function apply($number) {
if ($number % 3 == 0) {
return "Fizz";
}
return null;
}
}
// Strategia per Buzz
class Buzz implements FizzBuzzStrategy {
public function apply($number) {
if ($number % 5 == 0) {
return "Buzz";
}
return null;
}
}
// Classe che gestisce il contesto
class FizzBuzzContext {
private $strategies;
public function __construct() {
$this->strategies = [];
}
public function addStrategy(FizzBuzzStrategy $strategy) {
$this->strategies[] = $strategy;
}
public function process($number) {
foreach ($this->strategies as $strategy) {
$result = $strategy->apply($number);
if ($result !== null) {
return $result;
}
}
return $number;
}
}
// Esempio d'uso
$fizzBuzzContext = new FizzBuzzContext();
$fizzBuzzContext->addStrategy(new FizzBuzz());
$fizzBuzzContext->addStrategy(new Fizz());
$fizzBuzzContext->addStrategy(new Buzz());
for ($i = 1; $i <= 100; $i++) {
echo $fizzBuzzContext->process($i) . "\n";
}
Soluzione con il Chain of Responsibility Pattern
Usiamo ora invece il Chain of Responsibility Pattern per creare una catena di controllo che passa il numero attraverso diverse responsabilità (Fizz, Buzz, FizzBuzz).
// Interfaccia per il nodo della catena
abstract class FizzBuzzHandler {
protected $nextHandler;
public function setNext(FizzBuzzHandler $handler) {
$this->nextHandler = $handler;
}
abstract public function handle($number);
}
// Handler per FizzBuzz
class FizzBuzzHandlerConcrete extends FizzBuzzHandler {
public function handle($number) {
if ($number % 3 == 0 && $number % 5 == 0) {
return "FizzBuzz";
} else {
return $this->nextHandler ? $this->nextHandler->handle($number) : $number;
}
}
}
// Handler per Fizz
class FizzHandler extends FizzBuzzHandler {
public function handle($number) {
if ($number % 3 == 0) {
return "Fizz";
} else {
return $this->nextHandler ? $this->nextHandler->handle($number) : $number;
}
}
}
// Handler per Buzz
class BuzzHandler extends FizzBuzzHandler {
public function handle($number) {
if ($number % 5 == 0) {
return "Buzz";
} else {
return $this->nextHandler ? $this->nextHandler->handle($number) : $number;
}
}
}
// Esempio d'uso
$fizzBuzzHandler = new FizzBuzzHandlerConcrete();
$fizzHandler = new FizzHandler();
$buzzHandler = new BuzzHandler();
$fizzBuzzHandler->setNext($fizzHandler);
$fizzHandler->setNext($buzzHandler);
for ($i = 1; $i <= 100; $i++) {
echo $fizzBuzzHandler->handle($i) . "\n";
}
Soluzione con il Template Method Pattern
Ultima soluzione utilizzando Il Template Method Pattern, che permette di definire una struttura generale dell’algoritmo e lasciare alle sottoclassi il compito di implementare i dettagli (Fizz, Buzz, FizzBuzz).
// Classe astratta con il template method
abstract class FizzBuzzTemplate {
public function process($number) {
if ($this->isFizzBuzz($number)) {
return "FizzBuzz";
} elseif ($this->isFizz($number)) {
return "Fizz";
} elseif ($this->isBuzz($number)) {
return "Buzz";
} else {
return $number;
}
}
protected abstract function isFizzBuzz($number);
protected abstract function isFizz($number);
protected abstract function isBuzz($number);
}
// Implementazione concreta
class FizzBuzzGame extends FizzBuzzTemplate {
protected function isFizzBuzz($number) {
return $number % 3 == 0 && $number % 5 == 0;
}
protected function isFizz($number) {
return $number % 3 == 0;
}
protected function isBuzz($number) {
return $number % 5 == 0;
}
}
// Esempio d'uso
$fizzBuzzGame = new FizzBuzzGame();
for ($i = 1; $i <= 100; $i++) {
echo $fizzBuzzGame->process($i) . "\n";
}
Conclusione
In ciascuno di questi esempi, il problema FizzBuzz è stato risolto in modi diversi, mostrando come lo stesso problema possa essere affrontato utilizzando vari design patterns. La soluzione base è semplice e diretta, ma l’utilizzo di design patterns come Strategy, Chain of Responsibility o Template Method fornisce un approccio più strutturato, modulare e facilmente estendibile, specialmente per applicazioni più complesse.