Wednesday, August 11, 2010

FizzBuzz, rules engines and some fun

Yes, this old dog was recentlish introduced to Fizzbuzz, the developer 'test' that seemed to be making (or rather have made) ripples in the 'Oh My Lord, developers cannot do this' world (thanks a ton for this Brian). Yes, I realise this is a couple of years later, what can I say. Old dog, late to the party. For more on what FizzBuzz, Google has a plethora of sites with regards to this, as well as various solutions. But in short, FizzBuzz has the following one liner as a test:

Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.

Simple enough from the sound of things, apparently the controversy is with regards to the statement attached to it of "199 out of 200 programmers couldn't do it". Well, not one to shy away from a challenge, I thought that it would be cool to see whether I could implement FizzBuzz using our current rules engine technology. So with a bit of tinkering and some hocus-pocus I am proud to present the FizzBuzz solution as done in a rules engine. Yes, it is more verbose than it should be, but while I was doing it I thought to myself - "Self, wouldn't it be cool to provide a totally useless example of flowrules as well as the standard if... then rule syntax?". So without further ado...

Step1: The 'Business Model'
Well, I needed something that the rules will run on. It had to represent a number, it had to be incrementable, and due to a constraint in the rules engine, it had to be able to do a mod operation. I came up with the following very simple construct called imagineatively 'MyInteger'.

public class MyInteger {
private int myInt = 0;

public void setValue(int aValue)
{
myInt = aValue;
}

public int getValue()
{
return myInt;
}

public void increment()
{
myInt = myInt + 1;
}

public int mod(int aValue)
{
return myInt % aValue;
}
}


I also decided to make my life a teeny bit easier and provided an increment() convenience method. So, my business object in place, I was now ready to start playing within the rules engine IDE. Your milage may vary depending on the specific rules engine you want to use (as if you will ever get a question to do this). :)

Step2: The Rules Project
Let's look at the problem statement again, shall we?
Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.

Mmm, four rules spring to mind

  1. If the number is a multiple of three, print 'Fizz'

  2. If the number is a multiple of five print 'Buzz'

  3. if the number is a multiple of three as well as a multiple of five print 'FizzBuzz'

  4. if none of the above is true, print the number


Now, one could argue that rule (3) is simply an ordering of the output of rules (1) and (2). I made a decision to implement it as a seperate rule to stay within the spirit of printing "FizzBuzz". Yes, pedantic... And yes Brian, mod 15 will work as well...



So, this is what the four rules look like after they were defined


First we have rule 'Fizz'. Quite simple, really. I defined a precondition that one of the other rules (FizzBuzz) should not be satisfied. What the rules engine will do is to use the preconditions, along with priorities to construct the internal RETE tree. In this specific case if 'FizzBuzz is not satisfied (i.e. the value is not divisible by 3 and also not divisible by 5) this rule may fire.








































RuleName
Fizz
Status
Active
Effectivity
Always
Priority
Medium
Sub-Priority
50
Description
Display Fizz if the value is divisible by 3
Preconditions
Rule 'FizzBuzz' must not be satisfied
If
(za.co.passif.MyInteger Not Equals null
AND za.co.passif.MyInteger.mod(3) Equals 0 )
Then
Execute Method:: Print Message Fizz



The second rule is 'Buzz'. Similar to 'Fizz' in nature, except for the divisible by 5 bit.









































_______________________________________________________
RuleName
Buzz
Status
Active
Effectivity
Always
Priority
Medium
Sub-Priority
50
Description
Display Buzz if value is divisible by 5
Preconditions
Rule 'FizzBuzz' must not be satisfied
If
(za.co.passif.MyInteger Not Equals null
AND za.co.passif.MyInteger.mod(5) Equals 0 )
Then
Execute Method:: Print Message Buzz



The third rule simply prints out the value if none of the other rules have fired. Unfortunately the rules engine that is used does not have a 'good looking' print mechanism, so I had to cheat a bit and am printing a space and then the value.

















































_______________________________________________________
RuleName
NotFizzBuzz
Status
Active
Effectivity
Always
Priority
Medium
Sub-Priority
50
Description
Display the value if it is not divisible by 3 as well as not divisible by 5
Preconditions
Rule 'Buzz' must not be satisfied
Rule 'Fizz' must not be satisfied
Rule 'FizzBuzz' must not be satisfied
If
za.co.passif.MyInteger Not Equals null
Then
Execute Method:: Print +za.co.passif.MyInteger.getValue



And lastly the 'FizzBuzz' rule. You will notice there are no preconditions. It also has one other difference, namely I used the priority mechanism to ensure that this rule is executed first. It's priority is set to 100 where-as the other rules are set to a priority of 50.

































_______________________________________________________

RuleName
FizzBuzz
Status
Active
Effectivity
Always
Priority
Medium
Sub-Priority
100
Description
Display FizzBuzz if the value is divisible by 3 as well as divisible by 5
If
(za.co.passif.MyInteger Not Equals null
AND za.co.passif.MyInteger.mod(3) Equals 0
AND za.co.passif.MyInteger.mod(5) Equals 0 )
Then
Execute Method:: Print Message FizzBuzz



Step 3: Controlling the Flow

So this is great - we have a ruleset that can evaluate a single value and determine whether based on the value itself, it should print 'Fizz', 'Buzz', 'FizzBuzz' or the actual value. However it does not satisfy the requirement to be able to iterate through a set of numbers from 1 to 100. Now how to handle this? Well, since we are already overkilling the problem by using a rules engine, let's take it a step further and throw in the use of another tool in the rules engine arsenal, namely a FlowRuleSet. The FlowRuleset will define the flow and the iteration through the collection.

In overkill fashion, I managed to introduce not only three tasks, but a decision point as well. The tasks are responsible for incrementing the value, executing the four rules in the FizzBuzz ruleset and printing a pretty end statement. The decision point is used to determine whether the end value (100) has been reached.

Conclusion
Total Overkill (and yes, this worked like a charm). An enjoyable way to play around with the rules engine that we use - and I am not sure whether I can mention the name, thus it is not used. I am keen though to go through the same exercise with something like Drools. Or maybe see whether one can do this with a decisions table.


So, Brian, the gauntlet has been thrown down - care to come up with that more complex example of FizzBuzz that we discussed?

1 comment:

Ray said...

Nice! As you say, a Drools comparison to (insert_name_of_secret_rules_engine_here) would be interesting.