Categories
Clean Code

A checkbox must be read only once!

Boolean flags are bad. I would agree that a sheer ration between booleans and other data types can be quite a valid measurement of code quality. To support these statement I’d like you to read just two quotes:

Flag arguments are ugly. Passing a boolean into a function is a truly terrible practice. It immediately complicates the signature of the method, loudly proclaiming that this function does more than one thing. It does one thing if the flag is true and another if the flag is false!

Robert C. Martin, Clean Code

I dislike flag arguments because they complicate the process of understanding what function calls are available and how to call them. My first route into an API is usually the list of available functions, and flag arguments hide the differences in the function calls that are available.

Refactoring, Martin Fowler

Checkboxes seem to be a popular way to provide some user input. You may recognize some of these: isPremium, hasValue, isUpToDate.

Although I personally encourage everyone to avoid boolean as much as possible, I agree that often they seem to be a reasonable choice. But too often they live way too long.

This sample shows how a boolean user input dives into application code. You may foresee the upcoming disaster – when the application grows, new boolean flags will come and eventually the function will have nothing to do with Single Responsibility Principle.

class Program
{
    static void Main(string[] args)
    {
        var is3Dmode = args.Contains("3d");
        var objectToPrint = new ObjectToPrint()
        {
            Width = 10.0,
            Depth = 3.0,
            Height = 8.0
        };
        PrintObject(objectToPrint, is3Dmode);
    }

    static void PrintObject(ObjectToPrint objectToPrint, bool is3Dmode)
    {
        //some code

        System.Console.WriteLine($"The {nameof(objectToPrint.Width)} is: {objectToPrint.Width}");
        System.Console.WriteLine($"The {nameof(objectToPrint.Height)} is {objectToPrint.Height}");
        if (is3Dmode)
        {
            System.Console.WriteLine($"The {nameof(objectToPrint.Depth)} is {objectToPrint.Depth}");
        }

        //some code
    }
}

class ObjectToPrint
{
    public double Width { get; set; }
    public double Height { get; set; }
    public double Depth { get; set; }
}

There are design patterns that solve such problems. What I want to say is that it’s not about a design pattern – it’s about mindset. It’s about converting raw data to capable domain model as soon as you can.

class Program
{
    static void Main2(string[] args)
    {
        var is3Dmode = args.Contains("3d");
        ICanPrintObject printer = new ObjectPrinter2d();
        if (is3Dmode)
        {
            printer = new ObjectPrinter3d(printer);
        }
        var objectToPrint = new ObjectToPrint()
        {
            Width = 10.0,
            Depth = 3.0,
            Height = 8.0
        };
        PrintObject(objectToPrint, printer);
    }

    static void PrintObject(ObjectToPrint objectToPrint, ICanPrintObject printer)
    {
        //some code

        printer.Print(objectToPrint);

        //some code
    }
}

interface ICanPrintObject
{
    void Print(ObjectToPrint objectToPrint);
}

class ObjectPrinter2d : ICanPrintObject
{
    public void Print(ObjectToPrint objectToPrint)
    {
        System.Console.WriteLine($"The {nameof(objectToPrint.Width)} is: {objectToPrint.Width}");
        System.Console.WriteLine($"The {nameof(objectToPrint.Height)} is {objectToPrint.Height}");
    }
}

class ObjectPrinter3d : ICanPrintObject
{
    private ICanPrintObject ObjectPrinter { get; }

    public ObjectPrinter3d(ICanPrintObject objectPrinter2d)
    {
        this.ObjectPrinter = objectPrinter2d;
    }

    public void Print(ObjectToPrint objectToPrint)
    {
        this.ObjectPrinter.Print(objectToPrint);
        this.PrintDepthOf(objectToPrint);
    }

    private void PrintDepthOf(ObjectToPrint objectToPrint)
    {
        System.Console.WriteLine($"The {nameof(objectToPrint.Depth)} is {objectToPrint.Depth}");
    }
}

class ObjectToPrint
{
    public double Width { get; set; }
    public double Height { get; set; }
    public double Depth { get; set; }
}

As a result, we have a simple yet flexible object-oriented world which is not only easy to extend, but also easy to understand. And, what is more, we keep the ugly of primitive data types outside of the domain.

Leave a Reply

Your email address will not be published. Required fields are marked *