Classes and objects

CleanCode

Next part of my notes from Clean Code: A Handbook of Agile Software Craftsmanship, by Robert C. Martin.

clean_code_classes.png

Hiding implementation

Create a new class… add private fields (yes, yes, of course – we follow encapsulation)… add public getters and setters to all your private fields… STOP! There is a reason why you made your fields private in the first place, so keep them this way. Keep the freedom of changing types or implementation of your fields.

Hiding your implementation is not about building a layer of methods over the fields either. Hiding the implementation is about creating abstractions. A class should not just push the data through properties. It should let the consuming classes manipulate just the essence of data without knowing the actual implementation.

Take a look at the example below. In the first piece of code the actual gasoline level and tank capacity are exposed.

    interface Vehicle
    {
        double FuelTankCapacityInLitres { get; }
        double GasolineLevelInLitres { get; set; }
    }

Instead, we should just expose abstract percentage level of gasoline. Also Refuel method will internally take care of checking if tank capacity has not been reached yet while adding more fuel. Tank capacity does not need to be exposed publicly.

    interface Vehicle
    {
        double Refuel();
        double GetPercentFuelRemaining();

    }

Another topic related to hiding implementation is the law of Demeter. I wrote about it a separate post. See: The Law of Demeter.

Classes should be small

Classes should be small and follow Single Responsibility Principle. It is much better to have many small classes with distinct responsibilities than a big one. Do not be afraid that too many classes will affect readability. It is easier to find your stuff when it is organized in drawers than put all together in one big box!

Cohesion

A class should have a small number of instance variables and every method should manipulate at least one of them. If each method in a class uses all instance variables of this class, then this class is completely coherent. Usually it is not possible and is not even a good idea to create classes that are 100% coherent. However, when cohesion is high, methods and variables form together the logical whole.

In the example below only Size method does not use both variables. This class is very coherent.

    public class Stack
    {
        private int topOfStack;
        List elements = new List();

        public int Size()
        {
            return topOfStack;
        }

        public void Push(string element)
        {
            topOfStack++;
            elements.Add(element);
            Console.WriteLine("Pushed {0}: {1}", topOfStack, elements[topOfStack-1]);
        }

        public string Pop()
        {
            if (topOfStack == 0) throw new PoppedWhenEmpty();
            Console.WriteLine("Popped {0}: {1}", topOfStack, elements[topOfStack-1]);
            string element = elements[--topOfStack];
            elements.RemoveAt(topOfStack);
            return element;
        }
    }

 class Program
    {
        static void Main(string[] args)
        {
            Stack stack = new Stack();
            stack.Push("d");
            stack.Push("u");
            stack.Push("p");
            stack.Push("a");
            Console.WriteLine();
            stack.Pop();
            stack.Pop();
            stack.Pop();
            stack.Pop();
        }
    }

stack

The strategy of having small functions leads to having instance variables used only by a group of methods. That means this group should be extracted to another class! As a result, keeping high cohesion results in creating small classes.

References

“Clean Code: A Handbook of Agile Software Craftsmanship” – Robert C. Martin

Leave a comment