In the part 1 of the Article "Interfaces in C# for Beginners", I explained how interfaces work in C#.
In this article, "The Usage of Interfaces", I focus on what interfaces are and how interfaces are commonly used
in programs.
Warning: You need to have lot of patience to read and understand why interfaces are used. So be prepared :).
Let's first understand the meaning of the term Interface.
Meaning of Interface:
What are eyes? They are our interface which allow us to interface with the outside world and help us interpret what we see.
What are doors? They are the interfaces of a house which allow us to enter the house or go out of the house.
Thus, an interface is something that connects the outside world to the inside world. The interface is located at the
boundary of the system periphery that holds the interface.
There are sports matches played between two teams. When the two teams belong to different nations, matches between these two
teams are referred to as "Inter-National" matches. On the same lines, Interface = Inter-Face, which means between two faces.
The face of a human being provides a unique identity with which he/she can interact with the world. He/She can see the face of
other people and vice versa through the eyes that are located in front on the face periphery.
I hope this helps understand the meaning of the term "Interface."
Difference between Interface and Function
Now let's understand the difference between interface and function conceptually.
Interfaces can be unidirectional or bi-directional. Ears are an example of unidirectional Interfaces. Information flows from
the outside world to the inside world. The mouth is an example of a bi-directional interface. Information flows from the outside to the inside world
in the form of food and drinks while information flows from the inside world to the outside world in the form of speech and unwanted
food and drinks.
Thus, conceptually, the main purpose of an interface is to get information from the outside world into the system and to send the processed
information from inside world to the outside world. Conceptually, the main purpose of the functions is to process the information
received through the interfaces and return the processed information back to the interfaces. Thus the interfaces act as input/output sockets
while the functions act as processing engines of a factory/mixer grinder.
Consider a human being, let's call him Jack. He is a healthy individual. He intakes some food through his interface called a mouth. Someone looks at this and warns Jack that the food is poisonous. This warning hits Jack's ears which act as a unidirectional interface. The warning is processed by the functions executed by parts within his ears and the meaning is interpreted as the food is not good for him. In return he spits the food out of his mouth which here acts as a bi-directional interface.
Technically speaking, conceptually, an interface is supposed to connect the outside world to the inside world of the system and an interface
is located on the system periphery, whereas the Function is supposed to execute/process the information passed to it by the interface. This means,
the interface will capture the information from the outside world, send it to the functions, the functions will process the information and create a
meaning out of the information and accordingly create an impact in the form of a response emitted through other interfaces of the system.
Codewise, interfaces appear like functions. Hence the typical question, what is difference between an interface and a functions? :).
Difference between directly working with objects versus working through Interface
Now to understand the difference between directly working with an objects versus working with objects through interfaces.
Consider a sick patient. To cure him and heal him we feed him medicine, e.g. tablets through the interface called a "mouth."
The medicine in turn reaches the affected organ and cures it. This is like working through the interfaces. Now consider
that the patient is not getting cured even by medicines e.g. tablets. In such a case, the doctor conducts an operation where he/she
breaks open the body and directly work on the organ to fix the issue and make it working. This is like directly working with the object
and its functions. I hope this clarifies the difference between directly working with objects versus working with objects through interfaces.
It's always a good practice to have your application talk to the objects through interfaces rather than talking to the objects directly using functions. Consider the following class.
P1.cs
class Demo
{
public static void Main()
{
Home h = new Home();
h.MainDoor();
h.WindowDoor();
}
}
class Home
{
public void MainDoor()
{
System.Console.WriteLine("MainDoor");
}
public void WindowDoor()
{
System.Console.WriteLine("WindowDoor");
}
}
Instead of having the functions such as MainDoor
and WindowDoor
in class home, we could have defined them as Interface as shown in P2.cs
P2.cs
interface Doors
{
void MainDoor (bool switch);
void WindowDoor (bool switch);
}
class Demo
{
public static void Main()
{
Home h = new Home();
bool bOpen = true;
bool bClose = false;
Doors iDoors;
iDoors = h;
iDoors.MainDoor(bOpen);
iDoors.WindowDoor(bClose);
}
}
class Home : Doors
{
public void MainDoor(bool switch)
{
System.Console.WriteLine("MainDoor open" + switch);
}
public void WindowDoor(bool switch)
{
System.Console.WriteLine("WindowDoor open" + switch);
}
}
Note that in the above program we are talking to the home object via the interface iDoors
and not directly invoking MainDoor()
and WindowDoor()
even though it's possible.
h.MainDoor()
is also possible.
iDoors.MainDoor()
is also possible.
- But the latter [
iDoors.MainDoor()
] is more conceptual and desired than the former [h.MainDoor()
].
Using Interfaces to invoke functions from objects that belong to different classes
- Consider class A which has got a function
SortUsingQuickSortAlgorithm()
.
- Consider class B which had got a dunction
SortUsingBubbleSortAlgorithm()
.
- Let's say we need both the algorithms in Class C. We could do this by inheriting Class C from Class A and Class B.
What is the problem here? Well not here but there can be as illustrated below.
- Consider class O which has got a function
Sort()
and impliments sort algorithm using merge sort algorithm.
- Let's say we derive class A and class B from class O.
- Consider class A which has got a function
Sort()
and impliments sort algorithm using quick sort algorithm.
- Consider class B which has got a function
Sort()
and impliments sort algorithm using bubble sort algorithm.
- Let's say we need both the algorithms in Class C. We could do this by inheriting Class C from Class A and Class B.
- But there happens a collision also known as ambiguity between sort() of class A and sort() of class B inside
inheriting class C. This is avoided by having Interface as it helps to point to exact function that belongs to the class.
Thus, Common Processing Functions can also be a part of Interface. e.g. Build()
, Sort()
, Draw()
where the implementation of build/sort()/Draw() would differ from one class to other class.
Take the following example.
P3.cs
class LivingRoom
{
public void CloseDoor()
{
System.Console.WriteLine("LivingRoom");
}
}
class Kitchen
{
public void CloseDoor()
{
System.Console.WriteLine("Kitchen");
}
}
class Bedroom
{
public void CloseDoor()
{
System.Console.WriteLine("Bedroom");
}
}
class Balcony
{
public void CloseDoor()
{
System.Console.WriteLine("Balcony");
}
}
class App
{
LivingRoom obj1 = new LivingRoom ();
Kitchen obj2 = new Kitchen ();
Bedroom obj3 = new Bedroom();
Balcony obj4 = new Balcony();
obj1.CloseDoor();
obj2.CloseDoor();
obj3.CloseDoor();
obj4.CloseDoor();
}
In the above example we are directly working with the objects and its functions which are supposed to be interfaces because CloseDoor()
is not transforming any Data.
CloseDoor()
appears to be an operation on an interface like Door which connects the outside world to inside world and vice versa. The rectified program looks as follows.
P4.cs
Interface iDoor
{
void CloseDoor();
}
class LivingRoom: iDoor
{
public void CloseDoor()
{
System.Console.WriteLine("LivingRoom");
}
}
class Kitchen: iDoor
{
public void CloseDoor()
{
System.Console.WriteLine("Kitchen");
}
}
class Bedroom : iDoor
{
public void CloseDoor()
{
System.Console.WriteLine("Bedroom");
}
}
class Balcony: iDoor
{
public void CloseDoor()
{
System.Console.WriteLine("Balcony");
}
}
class App
{
LivingRoom obj1 = new LivingRoom ();
Kitchen obj2 = new Kitchen ();
Bedroom obj3 = new Bedroom();
Balcony obj4 = new Balcony();
iDoor iCloseDoor[] = {obj1, obj2, obj3, obj4};
for (int i=0;i<4;i++)
iCloseDoor[i].CloseDoor();
}
Now in the above example using the interface iDoor
, we are able to access the CloseDoor()
function present in any object belonging to different classes. An array of Interface can contain any objects belonging to any class that implements the interface.
Some Ideas of using the Interfaces
We can make use of interfaces for implementing multiple bill payment strategies. Let's say you want to be able to purchase a product and pay bill by Cash or by Credit card or by debit card, you can go for the use of interfaces as shown in the following example.
P5.cs
Interface Cashier
{
int payByCash(int amount);
int payByCreditCard(int cardNumber, int amount);
int payByDevitCard(int cardNumber, int amount);
}
class Shoes : Cashier
{
public int payByCash(int amount){};
public int payByCreditCard(int cardNumber, int amount){};
public int payByDevitCard(int cardNumber, int amount) {};
}
class Shirts : Cashier
{
public int payByCash(int amount){};
public int payByCreditCard(int cardNumber, int amount){};
public int payByDevitCard(int cardNumber, int amount) {};
}
class ClientApp
{
public static void main()
{
Shoes s1 = new Shoes();
Shoes s2 = new Shoes();
Shoes s3 = new Shoes();
Shirts sh1 = new Shirts();
Shirts sh2 = new Shirts();
Shirts sh3 = new Shirts();
Cashier Jack;
Jack = s1; Jack.payByCash(1000);
Jack = s2; Jack.payByCreditCard(123456,1000);
Jack = s3; Jack.payByDebitCard(7891011,1000);
Jack = sh1; Jack.payByCash(1000);
Jack = sh2; Jack.payByCreditCard(123456,1000);
Jack = sh3; Jack.payByDebitCard(7891011,1000);
}
}
We created an interface called as cashier because every product will come to client finally only through the cashier once we have made the payments.
So we will have three method prototypes in Interface Cashier viz payByCash
, pyByDebitCard
, and payByCreditCard
.
Next we have two classes viz Shoes
and Shirt
that impliment the Interface Cashier
. The code for these functions is kind of similar in both the classes. Next, inside the ClientApp, we create the objects of Shirt and Shoes and purchase them through Interface Cashier named as Jack.
There is lot of redundancy in this example because the code for payByCash
, pyByDebitCard
, and payByCreditCard
functions is kindof similar in both the classes
There is redundancy and maintenance. A change to one of the functions in one class had to be followed by similar change in the other class.
To avoid this we thought of using the interfaces in a different way as below.
P6.cs
Interface ClientsPaymentStrategy
{
int pay(int cardnumber, int amount);
}
class PayByCash : ClientsPaymentStrategy
{
public int pay(int cardNumber, int amount) {};
}
class PayByCreditCard : ClientsPaymentStrategy
{
public int pay(int cardNumber, int amount) {};
}
class PayByDebitCard : ClientsPaymentStrategy
{
public int pay(int cardNumber, int amount) {};
}
interface Cashier
{
int makePayment();
}
class Shoes : Cashier
{
ClientsPaymentstrategy p;
int Price;
Shirts ()
{
Price = 1000;
cardnumber = 0;
p = new PayByCash();
}
public SetPaymentMethod( int cardNumber, ClientsPaymentstrategy c)
{
p = c;
this.cardnumber = cardnumber;
}
public int makePayment()
{
p.pay(cardNumber, Price)
};
}
class Shirts : Cashier
{
ClientsPaymentstrategy p;
int Price;
int cardNumber;
Shirts ()
{
Price = 2000;
cardNumber = 0;
p = new PayByCash();
}
public SetPaymentMethod( int cardNumber, ClientsPaymentstrategy c)
{
p = c;
this.cardnumber = cardnumber;
}
public int makePayment()
{
p.pay(cardNumber, Price);
};
}
class Client
{
int cardNumber;
public static void main()
{
Shoes s1 = new Shoes();
Shoes s2 = new Shoes();
Shoes s3 = new Shoes();
Shirts sh1 = new Shirts();
Shirts sh2 = new Shirts();
Shirts sh3 = new Shirts();
purchaseProduct(s1);
purchaseProduct(s2);
s3.setPaymentMethod(cardnumber, new PayByDebitCard() );
purchaseProduct(s3);
purchaseProduct(sh1);
purchaseProduct(sh2);
sh3.setPaymentMethod( cardnumber, new PayByCreditCard() );
purchaseProduct(sh3);
}
private void purchaseProduct(Cashier Jack)
{
Jack.makePayment();
}
}
In the above program we have defined one interface called as ClientsPaymentStrategy
which has a prototype called pay()
. This interface is implemented by
Concrete classes called PayByCash
, PayByCreditCard
and PayByDebitCard
. Next we have Interface Cashier with one method prototype called makePayment()
.
This interface is implemented by classes Shirt
and Shoes
. Inside the Class Shirt
and Shoes
we have their respective prices defined. Each class has a
setPaymentMethod()
which sets the payment method by passing the object of concrete classes defined above. By default the payment method is set to payByCash
.
Inside the client App we are creating Shoes
and Shirt
Objects, and purchasing them through Cashier Jack using the make payment method which routs the call
to respective functions PayByCash
, PayByCreditCard
and PayByDebitCard
.
Thus interfaces are used to achieve LOOSE COUPLING and avoiding CODE REDUNDANCY very effectively. I hope this article was helpful for you.