DEV Community

Discussion on: FizzBuzz Refactoring Challenge with Open/Closed Principle

Collapse
olivermensahdev profile image
Oliver Mensah Author

That's awesome. I love this approach too. You can literally add a new requirement without writing a new class. Very elegant code but seems confused at first glance with the boolean flags been added. Maybe you can throw more light on the flag, why they exist?

Collapse
apmccartney profile image
Austin McCartney • Edited on

Sure Thing!

Let's consider a call to FizzBuzzBazzRule to see what that boolean flag is doing.

$FizzBuzzBazzRule->eval(5)

We begin by calling to the public facing eval method defined in the Rule abstract class.

public function eval(int $number)
{
    $this->apply($number, false);
}

This method forwards the argument to the protected apply method, but also passes a second boolean parameter False.

This boolean parameter is used to indicate whether any previous rules have successfully matched against the $number argument.

Because eval is called by the client, no previous rules have run and consequently, the $number has not yet matched against any previous rule.

abstract protected function apply(int $number, bool $match);

Note that the apply method is marked as abstract. That means that we'll need to determine what method to call by considering the child class of which the object is an instance. In this case, the child class is a Decorator with $name == 'Fizz' and $number == 3. So let's consider the apply method of the Decorator.

public function apply(int $number, bool $matched)
{
    if ($number % $this->number == 0) {
        echo $this->name;
        $this->nested_rule->apply($number, True);
    } else {
        $this->nested_rule->apply($number, $matched);
    }
}

We check whether the $number argument is divisible by the value of the $this->number member variable. 5 is not divisible by 3 so we take the else branch of the conditional. We pass forward the $number argument to the next rule along with the $matched boolean indicating whether any previous rule (which is still False).

The next rule is a Decorator with $name == 'Buzz' and $number == 5. 5 is divisible by 5 so we take the first branch of the conditional. Again we pass forward the $number argument to the next rule, but this time, because we've found a match, we pass True as the boolean argument.

The next rule is a Decorator with $name == 'Bazz' and $number == 7. 5 is not divisible by 7 so again we take the else branch of the conditional. We pass forward the $number argument to the next rule along with the $matched boolean indicating whether any previous rule (which is now True).

The last rule is a Bottom. This class serves as a base case for this sort of object-oriented recursion. Let's consider it's apply method.

public function apply(int $number, bool $matched)
{
    if(! $matched) { 
        printf('%d', $number);
    }
    echo PHP_EOL;
}

If no previous rule has matched the argument $number (as indicated by the $matched argument), we need to print the value of $number to the console. In either case, we print an end of line character to console to complete an iteration of the fizzbuzz loop.

Thread Thread
olivermensahdev profile image
Oliver Mensah Author

Thanks for the explanation. Very impressive. I checked your profile to get your Twitter account but it seems you are not on Twitter yet. I got to follow you so we can engage in discussions in the future.

Thread Thread
apmccartney profile image
Austin McCartney

twitter.com/RakishRhenoplos

Happy to chat anytime. =)