Designing Public Interfaces the Right Way
Tags: csharp, oop
Published: 2019-11-10 23:44:35


---
title: Designing Public Interfaces the Right Way
description: Interfaces define the ways objects interact with each other. Properly designing interfaces not only help conceptualize a system, but aid in testing and maintainability of our systems.
tags: csharp, oop, 
published: true
canonical_url: http://erikscode.space/index.php/2019/11/10/designing-public-interfaces-the-right-way/
cover_image: https://i0.wp.com/erikscode.space/wp-content/uploads/2019/11/kelly-sikkema-ECxsxbjAmMY-unsplash.jpg?fit=1987%2C3000
---
*Originally Posted at [eriksCodeSpace](http://erikscode.space/index.php/2019/11/10/designing-public-interfaces-the-right-way/)*

Interfaces define the ways objects interact with each other. Properly designing interfaces not only help conceptualize a system, but aid in testing and maintainability of our systems.

What Are Interfaces?
--------------------

This article is going to go beyond the concept of just using the principle of least privilege. We’re learning about this in the context of **good design** so you can write **maintainable** and **testable** code. The examples in this article are going to be in C# because I believe the syntax is optimal for explaining these concepts, but the concepts themselves transcend languages and apply to all OOP designs.

For now, put aside the idea of `interface` as a programming language construct; right now we’re going to use it in its regular sense. An “interface” is the point in which two entities meet and interact. For example, your kitchen sink’s interface is the hot and cold handles plus the faucet.

There are two entities: the sink and you. The sink provides a lot of functionality, but most of it is hidden from you. For example, you don’t need to know how the sink magically summons the water it spits out of the faucet; you don’t need to know how the sink is heating that water; and you definitely don’t need to know how the sink drains the water after it’s washed over your hands and dishes.

All you need to know as a user of the sink is how to turn the water on and off, and which dial gives you what kind of water. You, the calling entity, don’t know the inner workings of the sink, the subject entity. To put this back into a programming point of view, the sink interface’s functional signature would take in the hot and cold dials as arguments and return water.

Imagine if you had to know everything about plumbing just to wash your hands. Not only is this unrealistic, but it makes changing things nearly impossible. If the city implemented a new draining system, you’d have to relearn how that works before you can soak your dishes.

Likewise, this is exactly what happens when you have objects that are too tightly coupled. The calling object that knows too much about its subject is like the plumber washing their hands. The `public` interface of an object needs to be simple, and needs to change as infrequently as possible.

That last sentence is basically the main concept behind designing good interfaces: public interfaces should rarely, if ever, change.

Let’s Do an Example
-------------------

Like I said at the beginning of this article, our examples will use the C# programming language. I think the syntax for objects and their interfaces is the best one to illustrate the necessary examples.

We’re going to make a very simple command line RPG style fighting simulator. We’re going to have a `Hero` that attacks different types of `Enemies`. Let’s start by writing an abstract `Enemy` class:

```cs
namespace PubPriExample2
{
    public abstract class Enemy
    {
        public string name { get; set; }
        public int hitPoints { get; set; }
        public int strength { get; set; }
        public int defense { get; set; }
        public bool alive { get; set; }

        public Enemy(string enemyName, int hp, int str, int def)
        {
            name = enemyName;
            hitPoints = hp;
            strength = str;
            defense = def;
            alive = true;
        }

        public abstract void takeDamage(int attackDamage);
        public abstract bool isAlive();
        public abstract void die();
    }
}
```


What do we have here? This abstract class defines the base class for all enemies. Each enemy will have a name, hit points that represent how much damage it can take, strength which will control its attack damage, defense which controls how it takes damage, and a boolean variable called `alive` that will be set to false when the hit points fall below zero.

We also have a few methods. Besides the constructor, we have `takeDamage`, `isAlive`, and `die`. These are methods that every enemy must have because it is the only thing we’ll do with enemies in this game. All enemies will be attacked, will be alive or dead, and do something when they die.

Let’s make our `Hero` class. Most of this class will look like it should be extending the `Enemy` class, but we’re not going to do that. If this were to evolve as a full on RPG, `Hero` would probably also evolve into an abstract class used by multiple customizable heroes. For now, this class will just help us interact with the `Enemy` classes.

```cs
using System;

namespace PubPriExample2
{
    class Hero
    {
        public string name { get; set; }
        public int hitPoints { get; set; }
        public int strength { get; set; }
        public int defense { get; set; }

        public Hero(string heroName, int hp, int str, int def)
        {
            name = heroName;
            hitPoints = hp;
            strength = str;
            defense = def;
        }

        public void attack(Enemy enemy)
        {
            Console.WriteLine(name + " attacks " + enemy.name);
            enemy.takeDamage(strength);
        }
    }

}
```


We haven’t made any `takeDamage` type methods for this class because to illustrate the points I’m trying to make, they are not necessary. The one thing I do want you to note here is that is that `attack` takes in an `Enemy` object, and uses the enemy’s `takeDamage` method to interact with it. Hence, the `takeDamage` method is `Hero`‘s interface to `Enemy`.

Let’s make a couple of concrete enemy classes. First, we’ll make `RegularEnemy`:

```cs
using System;

namespace PubPriExample2
{
    class RegularEnemy : Enemy
    {
        public RegularEnemy(string enemyName, int hp, int str, int def) : base(enemyName, hp, str, def)
        {
        }

        public override void takeDamage(int heroStrength)
        {
            int damage = calculateDamage(heroStrength);
            hitPoints -= damage;
            Console.WriteLine(name + " takes " + damage + " points of damage");
            if (isAlive()) die();
        }

        public override bool isAlive() => hitPoints > 0;

        public override void die()
        {
            alive = false;
            Console.WriteLine(name + " has died!");
        }

        private int calculateDamage(int attackStrength)
        {
            int damage = attackStrength - defense;
            if (damage > 0)
            {
                return damage;
            }
            else
            {
                return 0;
            }
        }

    }
}

```


***NOTE:*** *If the* `=>` *syntax is throwing you off, don’t worry, it’s just some C# syntactic sugar.* `public override bool isAlive() => hitPoints > 0;` *is exactly the same as*

```cs
public override bool isAlive()
{
    return hitPoints > 0;
}
```


Here, we’ve overridden the base class’s abstract methods `takeDamage`, `isAlive`, and `die`. The one we’re going to pay attention to is the `takeDamage` method because, remember, it is the public interface at this moment.

Why, if we have `takeDamage`, do we also have `calculateDamage`? Why can’t we put the logic for calculatign the damage in the `takeDamage` method? We could, but this is part of what we’re learning.

First of all, calculating the damage within the same method in which we apply the damage would violate the **Single Responsibility Principle** of **S**OLID Object Oriented Design. We want each method to have a single responsibility, and the `takeDamage` method’s responsibility is applying damage, not calculating it.

Secondly, `takeDamage` is the interface that the `Hero` class is going to use to interact with `Enemy`, so we want it to be relatively uniform across all `Enemy` sub types. We will make other enemies that can also be attacked, but will take damage in different ways. `calculateDamage` is therefore the private method that will do the sub-type-specific operations to figure out how much our hero has damaged the enemy.

Speaking of, let’s make our second `Enemy` class, the `ArmoredEnemy`:

```cs
using System;

namespace PubPriExample2
{
    class ArmoredEnemy : Enemy
    {
        public ArmoredEnemy(string enemyName, int hp, int str, int def) : base(enemyName, hp, str, def)
        {
        }

        public override void takeDamage(int heroStrength)
        {
            int damage = calculateDamage(heroStrength);
            hitPoints -= damage;
            Console.WriteLine(name + " takes " + damage + " points of damage");
            if (!isAlive()) die();
        }

        public override bool isAlive() => hitPoints < 0;

        public override void die()
        {
            alive = false;
            Console.WriteLine(name + " has died; the armor shatters");
        }

        private int calculateDamage(int attackStrength)
        {
            // Enemy has 20% change of deflecting the attack
            Random random = new Random();
            int deflect = random.Next(1, 6);
            if (deflect == 1)
            {
                deflectAttack();
                return 0;
            }
            else if ((attackStrength - defense) > 0)
            {
                return (attackStrength - defense);
            }
            else
            {
                return 0;
            }
        }

        private void deflectAttack()
        {
            Console.WriteLine(name + " deflects the attack!");
        }
    }
}
```


Notice here that the `ArmoredEnemy` takes damage a different way. The enemy’s armor may deflect our hero’s attack, and thus the `calculateDamage` method essentially rolls the dice to find out if the attack was deflected or not.

Now, let’s see some actual use of these methods:

```cs
using System;

namespace PubPriExample2
{
    class Program
    {
        static void Main(string[] args)
        {
            Hero hero = new Hero("Erik", 100, 10, 5);
            RegularEnemy imp = new RegularEnemy("Imp", 50, 3, 2);
            ArmoredEnemy armorImp = new ArmoredEnemy("Armor Imp", 30, 4, 4);

            while (imp.alive)
            {
                hero.attack(imp);
            }

            while (armorImp.alive)
            {
                hero.attack(armorImp);
            }

            Console.ReadLine();
        }

    }
}
```


Run this and you should get something similar to the following output:

> Erik attacks Imp
> Imp takes 8 points of damage
> Erik attacks Imp
> Imp takes 8 points of damage
> Erik attacks Imp
> …truncated
> Imp takes 8 points of damage
> Imp has died!
> Erik attacks Armor Imp
> Armor Imp deflects the attack!
> Armor Imp takes 0 points of damage
> … truncated
> 
> Erik attacks Armor Imp
> Armor Imp deflects the attack! Armor Imp takes 0 points of damage Erik attacks Armor Imp Armor Imp takes 6 points of damage …truncated
> Erik attacks Armor Imp
> Armor Imp takes 6 points of damage
> Armor Imp has died; the armor shatters

Now consider we wanted to add even a third type of enemy, one that counterattacks. The code would mostly look the same, the new enemy would still have a `takeDamage` method, but there would also be a private method for implementing the counterattack. The interface for the enemy wouldn’t be any different, the hero can still only `attack`, but the implementation of that interface would be different, which would mean it should be hidden inside a private class of the new sub type.

### When Things Change

What if we wanted to add deflection capabilities to our `RegularEnemy` class? Just because an enemy isn’t armored doesn’t mean it can’t deflect an attack, right? Right. So we want to add deflection capabilities to `RegularEnemy` but we want these enemies to have a 5% chance of deflecting rather than 20%.

In theory, this new feature should not require ***any*** changes to our public interfaces. In fact, it doesn’t:

```cs
private int calculateDamage(int attackStrength)
        {
            // Enemy has 5% change of deflecting the attack
            Random random = new Random();
            int deflect = random.Next(1, 21);
            if (deflect == 1)
            {
                deflectAttack();
                return 0;
            }
            else if ((attackStrength - defense) > 0)
            {
                return (attackStrength - defense);
            }
            else
            {
                return 0;
            }
        }

        private void deflectAttack()
        {
            Console.WriteLine(name + " deflects the attack!");
        }
```


Nothing needed to change in order for this new feature to be added because we had a good separation of our public and private methods.

Interfaces Representing Roles
-----------------------------

It’s kind of funny to have spent all this time talking about interfaces and not once using the keyword `interface` huh? Let’s go ahead and do that, but let’s think it through first.

What is it about `Enemy` types that unify them? They all have stats and a name. They can also all be attacked, but in the context of a video game is that really a common trait? For example, consider a game like Zelda, where our hero Link can not only attack bad guys with his sword, but also grass and bushes. The grass and bushes don’t need stats like hit points and strength, but they’re still ***attackable***.

Attackable. That’s definitely not a real word, but it segues well into deciding when to use an abstract class vs when to use interfaces. Enemies are attackable. In Zelda, bushes are also attackable. So if we wanted to implement something similar in our game, like a tree our `Hero` could chop down with a sword, do we want the tree to extend the `Enemy` abstract class?

Conceptually, no. We could, of course do it this way, but making it work would look super shady. Likewise, any functionality we’d want to add to our sentient enemies would also get added to our non-sentient trees. In this case, trees and enemies don’t exactly share traits, but they do share roles. Specifically, they share the “attackable” role, which is what we’re going to name our interface. We’re going to name our interface `IAttackable` because that’s how you name interfaces in C#, sorry :shrug:

```cs
namespace PubPriExample2
{
    interface IAttackable
    {
        void takeDamage(int attackDamage);
        bool isAlive();
        void die();
    }
}
```


Now, head over to the `Enemy` abstract class to implement this interface:

```cs
namespace PubPriExample2
{
    public abstract class Enemy : IAttackable
    {
    // Truncated
```


What has this done for us? Haven’t we really just added some constraints on ourselves, forcing us to always write these three methods for any class implementing this interface?

Well, yes. And trust me, I get it. It does look like a bit of unnecessary self-restraint, but try to look at it from a bigger picture. Imagine you’re developing this game and you decided you wanted to add `attackable` trees a few months after you finished the last `Enemy` sub type. Will you remember which methods you’ve been using for these objects? In this case, probably yes because it’s such a small project right now, but imagine the project grows to a couple thousand lines of code.

Even better, imagine you are in a large company with multiple teams working on the game. Another team has been tasked with implementing trees that our `Hero` can cut down. Luckily, they already have this interface and know exactly how similar functionality has been added to the game before. It lets everything that is `attackable` in the game have the same interface.

Finally, it gives us a little something we might neglect. It helps us understand the system better. Think about `RegularEnemy` and `ArmoredEnemy` and how they both extend the `Enemy` class. Any time we look at a class, regardless of its name, if we see that it extends `Enemy`, we know that this is something the player will fight with.

Likewise, for any class implementing the `Attackable` interface, we will know it takes damage from the `Hero`. This example might make this benefit seem inconsequential, but in large systems with business processes less easy to conceptualize, this benefit can go a long way. The programmer’s main job is managing complexity, and things like proper naming and good interface design help us do that.

A Note on Testability
---------------------

We’ve learned that public interfaces should rarely change, and private ones are the real workhorses of classes. You might think then that the private methods of a class are the ones that need to be tested the most, but the opposite is true.

Private methods cannot be implemented by other classes, and the nuggets of functionality they provide are really just in support of their public methods. Therefore, thoroughly testing a class’s **public** interface essentially tests its private methods as well.

Similarly, as we’ve already learned, public interfaces should not change much. That means that testing of them should also not change very frequently, except when new functionality is added that changes the object’s behavior. For example, our `ArmoredEnemy` class implemented two private methods when taking damage. Testing the public `takeDamage` tests both methods satisfactorily. If we added even a third private method to the `takeDamage` method, we may need to change a few expected values here and there in our tests, but ultimately, test maintenance should be very low.

So not only does the proper design of interfaces help us conceptualize our systems better, it also helps cut down on maintenance costs while our tests still give us the same level of confidence they always have.

Summary
-------

Today we learned a little bit about good object oriented design. We learned that public interfaces, not necessarily `interface` methods, are designed to be stable. Keeping these methods constant and hiding complexity in private methods gives us the ability to add or modify functionality almost at a whim. These concepts give us simple public interfaces and help our test suite stay maintainable and effective.

For further reading, I strongly suggest checking out Sandi Metz’s book, [Practical Object-Oriented Design: An Agile Primer Using Ruby](http://www.informit.com/store/practical-object-oriented-design-an-agile-primer-using-9780134456478?ranMID=24808). She does a much better job at explaining this concept in chapter 4. The rest of the book is excellent as well. No this isn’t an affiliate link, I really think this book is worth reading if you’re interested in good software design.