September 15, 2020

Get better with interfaces

Get better with interfaces

This guide will cover the idea of interfaces, how to use them correctly and how to get better starting to think in abstractions rather than implementations.

The main language I’m using is C# - Which has good functionality of interfaces.
Other languages support it in various ways but the “Concept” of abstraction still stands.

What is an interface?

An interface is an abstract unit which acts as a contract.

An interface hides the implementation and forces it to implement a certain contract.
Thus creating the ability to remain abstract with precise API.

An example:

1
2
3
4
public interface IHeatable
{
Temprature Heat(Temprature);
}

The interface doesn’t give us any specific details, what are we heating? A plate? A room?
What kind of heat are we producing?
The implementation answers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class AirConditioner :IHeatable
{
private ACUnit mUnit;

public override Temprature Heat(Temprature temps)
{
mUnit.SetMode(ACUnit::Heat);
mUnit.SetTemprature(temps.Celsius);
return temps;
}
}

public class Oven : IHeatable
{
private Temprature mCurrentTemprature;

public override Temprature Heat(Temprature temps)
{
mCurrentTemprature = temps;
return mCurrentTemprature;
}
}

The idea of “Heat” is true for many electrical devices and non electrical.
For example I could implement a bonefire:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BoneFire : IHeatable
{
private Temprature mCurrentTemp;
private IList<Wood> mWood;

public override Temprature Heat(Temprature temps)
{
if(mCurrentTemp < temps)
{
mWood.Add(new Wood());
mCurrentTemp = temps;
}

return mCurrentTemp;
}
}

What is a good interface?

A good interface is behavior driven with explicit, readable and understandable API.
It cannot have:

It usualy has:

What a good interface looks like:

1
2
3
4
5
6
7
public interface ISensor<TData>
{
void Activate();
void Deactivate();

event Action<TData> OnAlarm;
}

What a bad interface looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface ILoadingManager
{
List<string> AllLines;

void Load(string fileName);
void Load(string url,bool otherMethod);
bool Unload(string fileName);

void Initialize();
void AfterInitialize(string configuration);

string ConvertLines();
string PublishLines();
}

Avoid Managers.

There is a strong tendency for software developers to “Manage” things.
This component doesn’t “manages the loading of lines and unlaoding of lines”.
It should perform “A synchronous load of lines from a source”.
Split behavior across different abstract units because they contain different behavior.

How to fix this interface?

The advantages to this approach are great:

How to avoid interface bloat

We can take this seperation of concerns and split ideas into tiny pieces of code.
This is not desirable because it creates complexity - not reduces it!

Personally I may be a perfectionist and caring for my craft - I want all the code to look tidy and seperated.
However I learned long ago a set of 2 words which answers my conflits: “Good enough”.
A design should not be perfect - it should be “good enough”.
However, Good enough for what?

Clear goals will make the design more understandable because you know what it should accomplish.
With additional guidelines it can create a more sustainable and maintanble code which will raise your code quality.

What I recommend:

Exercises

To get better you need to gather experience.
Complete these tasks if you want to examine your knowledge of interfaces:

  1. Make a list of viable interface in a Coffee Maker
    Open to see my answer
    • IWaterHeater - To contain heated water for the coffee.
    • ICoffeeContainer - Contains Coffee Beans.
    • ICoffeGrinder - Something needs to grind the coffee.
    • ICoffeeFilter - The filter that makes the coffee after grinding.
    • ISteamer - Additional Steam feature for steaming milk or similar products.

  1. We’ve required to refacotr an alarm system for houses. They have a monolithic interface which needs your refactor please help!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface IHouseAlarmHandler
{
void AddSensor(Sensor s);
void RemoveSensor(Sensor s);
List<Sensor> Sensors{ get; set;}

List<string> GetSensorNames();
Sensor GetSensorByName(string name);
Sensor GetSensorByID(string id);

void LoadConfiguration(HouseConfiguration);
void RunCheckOnSensors();
void PauseAlarm();
void ResumeActivity();

event Action<Sesnor> OnAlarm;
}
Open to see my answer
public interface IAlarmController
{
    bool PerformCheck();
    void PauseAlarm();
    void ResumeActivity();

    event Action<Sesnro> OnAlarm;
}

public class HouseAlarmController : IAlarmController
{
    private IList<Sensor> mSensors = new List<Sensor>();

    public HouseAlarmController(HouseConfiguration config){}
    // ... All other implementation
}

I’ve decided to contain the list of sensors in the implementation.
If the needs arises for adding or removing sensors through a different component,
we can use a factory or pass an IList interface.
I followed the term “Favor composition over inheritance”.

HouseConfiguration is a specific data of a house alarm controller.
Now we can implement different alarm systems!

I’ve renamed the method to “PerformCheck” to be more indicative.

  1. For your own fun - design a small server which handles requests for picture of kittens.
    Think of:

Thank you for reading!

על הפוסט

הפוסט נכתב על ידי Ilya, רישיון על ידי CC BY-NC-ND 4.0.

שתפו את הפוסט

Email Facebook Linkedin Print

קנו לי קפה

#Software