Make Expressive Interfaces

4 14
Avatar for StrungSafe
4 years ago

Naming is Hard

Asking to name something in programming reminds me of this statement:

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton

But why is naming so hard? I think it is because we desire to be descriptive, otherwise we would just call every variable a then b then c and so on. We want our variable to describe what they represent because then the code is easier to understand without having documentation/comments for every line of code. Sometimes though we are hard to find a good name because maybe we haven't abstracted the idea enough, what we are working on is not well understood, or maybe we are overthinking how this program or function will be used in the future.

Giving a variable a good name is the first step in creating an Interface that tells future developers (even future you) what the variable is intended for. Good naming will prevent frustration understanding the code and will inevitably lead to fewer bugs too because the purpose is clear with good naming.

An Interface

Sometimes I might refer to interface as (lower-case) interface or (upper-case) Interface. A (lower-case) interface is a programming language construct, a feature...this is what a class would implement. An (upper-case) Interface is what your consumers see, this includes variable names, classes, interfaces, or even the routes and data passed through an API.

An Interface, like variable naming, is the first set of documentation that future developers and consumers experience. A good Interface can even render additional documentation useless because the Interface is self explaining. This is the type of Interface we should be striving to achieve.

IDE's can make understanding Interfaces easier but we should not rely on them. For example, code reviews often can't provide the same level of details that a mature IDE will and having an Interface that is can be easily read will make the reviewers jobs easier and more enjoyable.

Express Intent

The Interfaces you create should express an intent. My example is a class that needs to expose a list of names. This list we expose should not be modifiable by consumers.

The example below exposes a list of names via an IList property called names. To adhere to the requirement to ensure consumers can't change the names list, we provide a copy of the list of names to the consumer. This interface does not explicitly tell the consumer that the names list can't be modified and so the consumer (without reading additional documentation) might think they should be able to add/delete/modify the list of names; which of course they will eventually find out they can't.

public class Example
{
    private readonly IList<string> names;

    public Example()
    {
        names = new List<string> { "John", "Mohammad" };
    }

    public IList<string> Names
    {
        get
        {
            var copy = new string[names.Count];
            names.CopyTo(copy, 0);
            return copy;
        }
    }
}

A more expressive example might use the system interface IReadOnlyCollection to tell consumers that the set of names being provided through the property is readonly and will not be available for editing.

public class ExpressiveExample
{
    public ExpressiveExample()
    {
        Names = new ReadOnlyCollection<string>(new List<string> { "John", "Mohammad" });
    }

    public IReadOnlyCollection<string> Names { get; }
}

Below we see the 'consumer' using both Example and ExpressiveExample. The consumer (without additional docs) is unable to tell they are unable to edit the names until runtime; this can lead to frustration while debugging. The developer will have to dig further to understand why the name change doesn't take hold.

However, when the consumer uses the ExpressiveExample the compiler is able to tell the consumer immediately that the names exposed is read-only which leads developers to know immediately know how names is expected to be used without using additional documentation to satisfy gaps in knowledge.

public class Program
{
    public static void Main(string[] args)
    {
        var example = new Example();

        Console.WriteLine("The example contains the names:");

        foreach (string exampleName in example.Names)
        {
            Console.WriteLine($"\t{exampleName}");
        }

        example.Names[0] = "Fred";

        Console.WriteLine("Trying to overwrite the first name with Fred");
        Console.WriteLine($"The first name is: {example.Names[0]}");
        // The name does not get overwritten

        var expressiveExample = new ExpressiveExample();

        Console.WriteLine("The expressive example contains the names:");

        foreach (string exampleName in expressiveExample.Names)
        {
            Console.WriteLine($"\t{exampleName}");
        }

        //expressiveExample.Names[0] = "Fred";
        Console.WriteLine("The compiler prevents us from even attempting to overwrite the first name");
    }
}

Conclusion

Making a great Interface is hard...but taking the time to have a great and intuitive Interface is important to ensure your code base is easy to understand which will lead to less frustration and bugs.

2
$ 0.01
$ 0.01 from @TheRandomRewarder
Avatar for StrungSafe
4 years ago

Comments

nice

$ 0.00
4 years ago

amazing article 😍

$ 0.00
4 years ago

Making a great Interface is hard...but taking the time to have a great and intuitive Interface is important......absolutely true

$ 0.00
4 years ago