Interfacing in C#
Jul 26, 2019 • 10 Minute Read
Introduction
The interfaces in OOP programming are a mysterious topic. Every seasoned programmer you ask will give you an explanation that is a bit different, that depends on their field of experience and their use of this topic. The most common pattern in these answers is that the interface is a concept of abstraction and encapsulation. Let's look at this topic through the concept of an electric longboard and it's controller.
The Controller and the Longboard
This device has a small controller with two buttons and a rotating switch. One button serves as a turn on/off button, the other puts the engine in reverse (actually changing the flow of the current). The rotating switch will accelerate the board if it's pushed upwards and brake the wheels if pushed downwards. The acceleration and deceleration are proportional to the length of the rotation around the circumference of the switch, as it can fine-tune the speed of the vehicle.
The Controller Box
The current box we have, namely the controller, defines specific inputs it can process and they have a result as the output which is produced for a given input. Just like the power-on, power-off, accelerate, decelerate, etc... Some rare cases also specify the return types. In our example, the return type is not relevant as we want to control the state of the board only.
These interfaces have no restriction over what semantics these operations are. It's an accepted best practice to document them in proximity and pick good naming conventions.
To be more specific, we can only accelerate or decelerate a board if it is turned on. This is based on the assumption that our board will only be driven by the electric engine, and not on a downhill. The wheels can rotate even when it's turned off, but the brakes only work if the power is on.
Criteria for the Demo
Here are some rules based on which the demo is implemented:
- You cannot turn the device ON twice.
- You cannot turn the device OFF twice.
- You can only turn the device OFF if it's still.
- You can only accelerate a turned on device, and only until it reaches the maximum speed specified at initialization.
- You can only decelerate a device until it comes to a halt!
IControllable
There is a convention that you should prefix your interfaces with the I character which clearly marks the intention and helps the programmer to better understand the code.
This is what our interface definition looks like:
namespace InterfaceTutorial
{
interface IControllable
{
string State { get; set; }
void Accelerate();
void Decelerate();
}
}
We have an exposed State property and the contract for the Accelerate and Decelerate functions.
In this demo, we are collecting everything under the InterfaceTutorial namespace to make it easier to reference.
Vehicle
This class implements the interactions with our device. The class definition shows you the interface which allows interaction.
class Vehicle : IControllable
We have a constructor which initializes the board with default parameters, in case they are good enough.
public Vehicle(string name = "No Name", int maxspeed = 0, int speed = 0, string state = "off")
{
BoardName = name;
MaxSpeed = maxspeed;
Speed = speed;
State = state;
}
We have four exposed properties.
public string BoardName { get; set; }
public int MaxSpeed { get; set; }
public int Speed { get; set; }
public string State { get; set; }
Let's look at our PowerOn method. As the criteria are set, we only allow power On once.
public void PowerOn()
{
if(State == "on")
{ Console.WriteLine($"The board: {BoardName} is already ON!"); }
else
{
State = "on";
Console.WriteLine($"Powering ON the Board: {BoardName}, currently at {Speed} MPH!");
}
}
The power OFF also meets the expectations. The criteria of not powering off the board twice and we are not allowed to power off the moving vehicle.
public void PowerOff()
{
if(State == "off")
{ Console.WriteLine($"The board: {BoardName} is already OFF!"); }
else {
if(Speed > 0)
{ Console.WriteLine($"You are not allowed to Power OFF a moving board: {BoardName}"); }
else {
State = "off";
Console.WriteLine($"Powering OFF the board: {BoardName}, currently at {Speed} MPH!");
}
}
}
The accelerate and decelerate functions are very similar in this sense. Both restrict the actions to a powered on device, and the change of speed is kept between 0 and the maximum speed specified at instantiation time.
public void Accelerate()
{
if(State == "off")
{ Console.WriteLine($"The board needs to be turned on to pick up speed!"); }
else
{
if (Speed < MaxSpeed)
{
Speed += 10;
Console.WriteLine($"The {BoardName} accelerates towards {Speed} MPH!");
}
else
{ Console.WriteLine($"The {BoardName} reached it's speed limit, continue journey at {MaxSpeed} MPH!"); }
}
}
public void Decelerate()
{
if (State == "off")
{ Console.WriteLine($"The {BoardName} is currently {State}, please dont use it this way as the breaks are not functional!"); }
else
{
if(Speed <= 0)
{
Console.WriteLine($"The {BoardName} has lost all it's speed, came to a halt!");
}
else
{
Speed -= 10;
Console.WriteLine($"The {BoardName} decelerates towards {Speed} MPH!");
}
}
}
This sections code needs to be wrapped inside the following code-block. Otherwise, our calls will need to be adjusted to work properly.
using System;
namespace InterfaceTutorial
{
}
Test Drive
Now that we have completed our demonstration about how you can use your interface to control the board. First, we create the Vehicle instance. This takes two arguments; the first is the name we would like to use andthe second is the maximum speed. Note that, during initialization, this value is 0. So, if you don’t specify the limit, you can’t ride the board.
using System;
namespace InterfaceTutorial
{
class Program
{
static void Main(string[] args)
{
Vehicle meepo = new Vehicle("Meepo V2", 40);
if (meepo is IControllable)
{
meepo.Decelerate();
meepo.PowerOn();
meepo.PowerOn();
meepo.Accelerate();
meepo.Accelerate();
meepo.PowerOff();
meepo.Accelerate();
meepo.Accelerate();
meepo.Accelerate();
meepo.Decelerate();
meepo.Decelerate();
meepo.Decelerate();
meepo.Decelerate();
meepo.Decelerate();
meepo.PowerOff();
meepo.PowerOff();
}
else
{
Console.WriteLine($"The {meepo.BoardName} can't be driven");
}
Console.ReadLine();
}
}
}
Then we will check if the device implements the necessary contracts and take it for a ride.
Running the solution will produce this output:
The Meepo V2 is currently off, please don’t use it this way as the breaks are not functional!
Powering ON the Board: Meepo V2, currently at 0 MPH!
The board: Meepo V2 is already ON!
The Meepo V2 accelerates towards 10 MPH!
The Meepo V2 accelerates towards 20 MPH!
You are not allowed to Power OFF a moving board: Meepo V2
The Meepo V2 accelerates towards 30 MPH!
The Meepo V2 accelerates towards 40 MPH!
The Meepo V2 reached its speed limit, continue journey at 40 MPH!
The Meepo V2 decelerates towards 30 MPH!
The Meepo V2 decelerates towards 20 MPH!
The Meepo V2 decelerates towards 10 MPH!
The Meepo V2 decelerates towards 0 MPH!
The Meepo V2 has lost all its speed, came to a halt!
Powering OFF the board: Meepo V2, currently at 0 MPH!
The board: Meepo V2 is already OFF!
Note, as before, upon the first call to the PowerOn() function we have a warning saying the breaks won’t work.
After the power on happens, we are able to accelerate, then a call to the PowerOff() is prevented because the device is not still. Then we accelerate a little more and, finally, we reach the limit and decelerate to a halt. Calling the PowerOff() twice is also handled by our interface.
Conclusion
This demonstration showed you the example implementation of an interface that allowed you to control a board. Naturally, this could be used by any class that implements the necessary contract details. The demo also highlighted how these work in tandem and gave you some ideas as to the extension of this functionality. I have shown you some semantic error handling which will come in handy for your future projects.
All in all, just like how a car an interface helps you interact with your environment by reducing or controlling the types of input that a system can take and gives you expected outputs. When you turn the steering wheel left the car goes left, when you turn it right it goes right. You don’t have to care about the internal workings of the gearboxes and other parts of your car that allow this movement.