I'm not familiar with PHP, but this seems less than ideal. It broke my heart to see you manually combinate the predicates in FizzBuzzRule, FizzBazzRule, BuzzBazzRule, and FizzBuzzBazzRule.
Are you familiar with decorator pattern? I think it expresses this sort of use case pretty elegantly. I took a few minutes to jot down an alternative implementation to demonstrate.
there are fewer class definitions (3 vs 9) and less code overall (59 vs 134) to maintain and test.
adding a new check (e.g. values divisible by 11 print bar) does not require defining a new class. The implementation you suggest seems to require defining a combinatorially increasing number classes to accommodate a new check.
reordering the checks does require defining a new class, just a new instance, e.g.
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?
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.
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.
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.
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.
I'm not familiar with PHP, but this seems less than ideal. It broke my heart to see you manually combinate the predicates in
FizzBuzzRule
,FizzBazzRule
,BuzzBazzRule
, andFizzBuzzBazzRule
.Are you familiar with decorator pattern? I think it expresses this sort of use case pretty elegantly. I took a few minutes to jot down an alternative implementation to demonstrate.
This snippet has the expected output of
Using a decorator this way has a few advantages:
11
printbar
) does not require defining a new class. The implementation you suggest seems to require defining a combinatorially increasing number classes to accommodate a new check.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?
Sure Thing!
Let's consider a call to
FizzBuzzBazzRule
to see what that boolean flag is doing.We begin by calling to the public facing
eval
method defined in theRule
abstract class.This method forwards the argument to the protected
apply
method, but also passes a second boolean parameterFalse
.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.Note that the
apply
method is marked asabstract
. 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 aDecorator
with$name == 'Fizz'
and$number == 3
. So let's consider the apply method of theDecorator
.We check whether the
$number
argument is divisible by the value of the$this->number
member variable.5
is not divisible by3
so we take theelse
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 stillFalse
).The next rule is a
Decorator
with$name == 'Buzz'
and$number == 5
.5
is divisible by5
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 passTrue
as the boolean argument.The next rule is a
Decorator
with$name == 'Bazz'
and$number == 7
.5
is not divisible by7
so again we take theelse
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 nowTrue
).The last rule is a
Bottom
. This class serves as a base case for this sort of object-oriented recursion. Let's consider it'sapply
method.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 thefizzbuzz
loop.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.
twitter.com/RakishRhenoplos
Happy to chat anytime. =)