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.
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 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.
Here are some rules based on which the demo is implemented:
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:
1namespace InterfaceTutorial
2{
3 interface IControllable
4 {
5 string State { get; set; }
6 void Accelerate();
7 void Decelerate();
8 }
9}
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.
This class implements the interactions with our device. The class definition shows you the interface which allows interaction.
1class Vehicle : IControllable
We have a constructor which initializes the board with default parameters, in case they are good enough.
1public Vehicle(string name = "No Name", int maxspeed = 0, int speed = 0, string state = "off")
2 {
3 BoardName = name;
4 MaxSpeed = maxspeed;
5 Speed = speed;
6 State = state;
7 }
We have four exposed properties.
1 public string BoardName { get; set; }
2 public int MaxSpeed { get; set; }
3 public int Speed { get; set; }
4 public string State { get; set; }
Let's look at our PowerOn method. As the criteria are set, we only allow power On once.
1public void PowerOn()
2 {
3 if(State == "on")
4 { Console.WriteLine($"The board: {BoardName} is already ON!"); }
5 else
6 {
7 State = "on";
8 Console.WriteLine($"Powering ON the Board: {BoardName}, currently at {Speed} MPH!");
9 }
10 }
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.
1public void PowerOff()
2 {
3 if(State == "off")
4 { Console.WriteLine($"The board: {BoardName} is already OFF!"); }
5 else {
6 if(Speed > 0)
7 { Console.WriteLine($"You are not allowed to Power OFF a moving board: {BoardName}"); }
8 else {
9 State = "off";
10 Console.WriteLine($"Powering OFF the board: {BoardName}, currently at {Speed} MPH!");
11 }
12 }
13 }
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.
1 public void Accelerate()
2 {
3 if(State == "off")
4 { Console.WriteLine($"The board needs to be turned on to pick up speed!"); }
5 else
6 {
7 if (Speed < MaxSpeed)
8 {
9 Speed += 10;
10 Console.WriteLine($"The {BoardName} accelerates towards {Speed} MPH!");
11 }
12 else
13 { Console.WriteLine($"The {BoardName} reached it's speed limit, continue journey at {MaxSpeed} MPH!"); }
14 }
15 }
16
17
18 public void Decelerate()
19 {
20 if (State == "off")
21 { Console.WriteLine($"The {BoardName} is currently {State}, please dont use it this way as the breaks are not functional!"); }
22 else
23 {
24 if(Speed <= 0)
25 {
26 Console.WriteLine($"The {BoardName} has lost all it's speed, came to a halt!");
27 }
28 else
29 {
30 Speed -= 10;
31 Console.WriteLine($"The {BoardName} decelerates towards {Speed} MPH!");
32 }
33 }
34 }
This sections code needs to be wrapped inside the following code-block. Otherwise, our calls will need to be adjusted to work properly.
1using System;
2
3namespace InterfaceTutorial
4{
5}
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.
1using System;
2
3namespace InterfaceTutorial
4{
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 Vehicle meepo = new Vehicle("Meepo V2", 40);
10
11 if (meepo is IControllable)
12 {
13 meepo.Decelerate();
14 meepo.PowerOn();
15 meepo.PowerOn();
16 meepo.Accelerate();
17 meepo.Accelerate();
18 meepo.PowerOff();
19 meepo.Accelerate();
20 meepo.Accelerate();
21 meepo.Accelerate();
22 meepo.Decelerate();
23 meepo.Decelerate();
24 meepo.Decelerate();
25 meepo.Decelerate();
26 meepo.Decelerate();
27 meepo.PowerOff();
28 meepo.PowerOff();
29 }
30 else
31 {
32 Console.WriteLine($"The {meepo.BoardName} can't be driven");
33 }
34 Console.ReadLine();
35 }
36 }
37}
Then we will check if the device implements the necessary contracts and take it for a ride.
Running the solution will produce this output:
1The Meepo V2 is currently off, please don’t use it this way as the breaks are not functional!
2Powering ON the Board: Meepo V2, currently at 0 MPH!
3The board: Meepo V2 is already ON!
4The Meepo V2 accelerates towards 10 MPH!
5The Meepo V2 accelerates towards 20 MPH!
6You are not allowed to Power OFF a moving board: Meepo V2
7The Meepo V2 accelerates towards 30 MPH!
8The Meepo V2 accelerates towards 40 MPH!
9The Meepo V2 reached its speed limit, continue journey at 40 MPH!
10The Meepo V2 decelerates towards 30 MPH!
11The Meepo V2 decelerates towards 20 MPH!
12The Meepo V2 decelerates towards 10 MPH!
13The Meepo V2 decelerates towards 0 MPH!
14The Meepo V2 has lost all its speed, came to a halt!
15Powering OFF the board: Meepo V2, currently at 0 MPH!
16The 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.
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.