Saturday 14 April 2012

Chain Of Responsibility: Why Can't Programmers Program?

According to CodingHorror he was battling to understand why:

Like me, the author is having trouble with the fact that 199 out of 200 applicants for every programming job can't write code at all. I repeat: they can't write any code whatsoever.

In order to make sure that the applicant could write code they would ask them to perform a simple task:

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".

Most good programmers should be able to write out on paper a program which does this in a under a couple of minutes.

But Apparently:

Want to know something scary? The majority of comp sci graduates can't. I've also seen self-proclaimed senior programmers take more than 10-15 minutes to write a solution.

So apparently a number of companies use this question to quickly eliminate those who can't write any code. When I first heard of this I was surprised and thought what is the catch.

Would the person asking this be expecting some really fancy implementation?

But what the person is really looking for is to write it so that it just works exactly as requested

You can look at this page or search for examples which are available in just about every language out there, but here is the C# code:

public void Main()
{
    for (int
i = 1; i <= 100; i++)
    {
        FizzBuzz(i);
    }
}


public void FizzBuzz(int
value)
{
    if
(value % 3 == 0 && value % 5 == 0)
    {
        Console.WriteLine("FizzBuzz"
);
    }
    else if
(value % 3 == 0)
    {
       Console.WriteLine("Fizz"
);
    }
    else if
(value % 5 == 0)
    {
       Console.WriteLine("Buzz"
);
    }
    else
    {
       Console
.WriteLine(value);
    }
}

Now you can certainly try and get more fancy about this and some have certainly done so, but I thought it would be valuable to be able to write this and then say what fundamental principle are you violating and how to solve it.

The Chain Of Responsibility

The above example can be thought of in a real world application with various conditions for each of the inputs and providing a different implementation for each. And in this case this is a violation of a fundamental principle called the Open/closed principle

And in the above implementation we might want to add a check for the number divisible by 10 and print out Bazz. This certainly means our function is not Closed for modifications as we would have to add another code block and modify the if-else logic.

One solution to this problem would be to create one strategy per implementation and use the Chain-of-responsibility pattern

First create a printer object for any ancestor in the chain:

public abstract class Printer
{
    private Printer
_Next;
    public Printer SetNext(Printer
next)
    {
       this
._Next = next;
       return
next;
    }

    protected abstract bool HandlePrint(int
value);

    public virtual bool Print(int
value)
    {
        if (!this
.HandlePrint(value))
        {
           if (this._Next != null
)
           {
              return this
._Next.Print(value);
           }
           return false
;
        }
        return true
;
    }
}

The abstract method HandlePrint gets a value and it is up to the strategy to decide whether it supports the given value and handles the request. It can either stop processing or let the processing continue further down the chain.

And then we would have an implementation for each of the printing strategies:

public class FizzBuzzPrinter : Printer
{
    protected override bool HandlePrint(int
value)
    {
       if
(value % 3 == 0 && value % 5 == 0)
       {
           Console.WriteLine("FizzBuzz"
);
           return true
;
        }
        return false
;
    }
}


public class FizzPrinter : Printer
{
    protected override bool HandlePrint(int
value)
    {
        if
(value % 3 == 0)
        {
            Console.WriteLine("Fizz"
);
            return true
;
        }
        return false
;
    }
}


public class BuzzPrinter : Printer
{
    protected override bool HandlePrint(int
value)
    {
       if
(value % 5 == 0)
       {
           Console.WriteLine("Buzz"
);
           return true
;
       }
       return false
;
    }
}

Also we can now very easily add the ability to check for 10 and print Bazz by registering a new handler.

public class BazzPrinter : Printer
{
    protected override bool HandlePrint(int
value)
    {
        if
(value % 10 == 0)
        {
            Console.WriteLine("Bazz"
);
            return true
;
        }
        return false
;
     }
}

Now our implementation of our printing engine can be closed to modifications:

public void Main()
{
    Printer printer = new FizzBuzzPrinter
();
        printer
          .SetNext(
new BazzPrinter
())
          .SetNext(
new FizzPrinter
())
          .SetNext(
new BuzzPrinter
());

// Add bazz printer

    for (int
i = 1; i <= 100; i++)
    {
        Console.Write(string.Format("{0}:"
, i));

        if
(!printer.Print(i))
        {
            Console
.WriteLine();
        }
     }
}

image

So there you have it the solution can and is simple initially provided you know what the potential violation is and problems that can be caused down the line. And once you made more than 2-3 modifications to it its time to think about refactoring it.

Live writer was harmed 5 times during the making of this post.

No comments:

Post a Comment