Categories
Software Development

How to build better public APIs for your classes

When defining the public APIs of your classes, you need to consider whether a property or method you chose to expose is helping the consumers address an immediate goal in full.

When defining the public APIs of your classes, you need to consider whether a property or method you chose to expose is helping the consumers address an immediate goal in full.


API is not just a means for two systems to communicate over the wire. The Application Programming Interface is any code that consumers can interact with. So, any “visible” methods, properties, or even fields (ugh) fit the criteria.

Consider this version of the Client class.

public class Client
{
    public string Name { get; set; }
    public string NormalizeName(string name)
    {
        name = (name ?? "").Trim();
        if (name.Length > 50)
            name = name.Substring(0, 50);
        return name;
    }
}

The NormalizeName method is marked as public, but it is definitely an implementation detail, something the consumers should not know about.

Consumers wanting to set a client’s name must call NormalizeName before setting the Name property. Two steps are required to do something that should be atomic.

Moreover, they must know they are expected to call it. You can be sure that someone will forget to call it in the future, maybe yourself.

public class CodeFromOutsideLayer
{
    public void ChangeClientName(string newName)
    {
        var client = database.GetClient(clientId);
        // Two operations to accomplish one thing.
        newName = client.NormalizeName(newName);
        client.Name = newName;
    }
}
Cluttered public APIs will make your software look like this

A better way

A better version of the Client class would be:

public class Client
{
    private string name;
    public string Name
    {
        get { return name; }
        set { name = NormalizeName(value); }
    }
    private string NormalizeName(string name)
    {
        name = (name ?? "").Trim();
        if (name.Length > 50)
            name = name.Substring(0, 50);
        return name;
    }
}

The Client class is part of the Domain Model, so it should be the one responsible for enforcing its invariants.

Now, there’s is no way for a consumer to set the name without first normalizing it.

Also, the consumer code is now simpler and focused on what it wants to do: set the client’s name.

public class CodeFromOutsideLayer
{
    public void ChangeClientName(string newName)
    {
        var client = database.GetClient(clientId);
        client.Name = newName;
    }
}
Well designed public APIs look like this

Rules of thumb

For a method or property to be considered part of your classes’ public APIs, you must make sure that it:

  • Is used by code from the outside layer;
  • Addresses one of the atomic goals the consumer has to accomplish.

Hide code that is not used by the outside layer

If a method is not called from the outside, it should be marked as private or internal.

Exposing methods, or even properties, when no one is using them is bad. The guideline is to limit the options. Remember the YAGNI principle.

Start with private and internal, and only change to public if the need arises.

Expose only atomic operations

The steps needed to reach a single goal are implementation details and should not be visible.

Your method may call into a hundred others to deliver a single result, but consumers should not know about it. They don’t care, they shouldn’t need to.

Conclusion

It may seem trivial, but it is the basic things done right from the very beginning that make the difference in the long run.

By Phillippe Santana

Passionate about writting code that people can understand, I'm a software developer, a project manager, an entrepreneur, and people/culture enthusiast. Find me on [Linkedin](https://www.linkedin.com/in/phillippesantana/) and on [Medium](https://medium.com/@phillippesantana).