In object-oriented programming, applications are often designed as complex hierarchies of classes, which are designed to model as closely as possible to the business object we are dealing with. You reuse existing code by inheriting it from an existing base class and overriding its methods. When you derive a subclass from a base class, you must be well aware of the implementation details of the base class. For example, what is the side effect of changing the value of a member variable? How does it affect the code in the base class? Will overriding a base class method and providing a different behavior break the code of clients that expect the base behavior?
This form of reuse is commonly known as white-box reuse, because you are required to be familiar with the details of the base class implementation. White-box reuse simply doesn't allow for economy of scale in large organizations' reuse programs or easy adoption of third-party frameworks.
Interface-oriented programming promotes black-box reuse, which allows you to use an existing component without worrying about its internals implementation, as long as the component complies with some predefined set of operations that are defined in interface.
Thus using Interface-oriented programming we can truely develop a plug and play component for our application. Let us assume we are writing a database application and we are not sure which database our client will be using or our application can be configured to adapt to different databases. Below is the class diagram showing how it can be achieved using interface.
public interface IDataProvider
{
void AddItem(int itemId, string itemName, string description);
IDataReader GetItem(int itemId);
IDataReader GetItems();
void UpdateItem(int itemId, string itemName, string
description);
void DeleteItem(int itemId);
}
public class SqlDataProvider: IDataProvider
{
public void AddItem(int itemId, string itemName, string
description)
{
// implement the method specific to sql server
}
public IDataReader GetItem(int itemId)
{
// implement the method specific to sql server
}
public IDataReader GetItems()
{
// implement the method specific to sql server
return null;
}
public void UpdateItem(int itemId, string itemName, string
description)
{
// implement the method specific to sql server
}
public void DeleteItem(int itemId)
{
// implement the method specific to sql server
}
}
public class OracleDataProvider: IDataProvider
{
public void AddItem(int itemId, string itemName, string
description)
{
// implement the method specific to oracle
}
public IDataReader GetItem(int itemId)
{
// implement the method specific to oracle
return null;
}
public IDataReader GetItems()
{
// implement the method specific to oracle
return null;
}
public void UpdateItem(int itemId, string itemName, string
description)
{
// implement the method specific to oracle
}
public void DeleteItem(int itemId)
{
// implement the method specific to oracle
}
}
Now in the main method we can get the database value from configuration settings and depending on the database setting we can create the DataProvider of our choice. This model definetly has an benefit over inheritance where our Business object would have largely been dependent on the Database Access layer. Here with this model the client can easily switch between different database or even create there own Data Provider component if new Database has come in market and the component can be easily plugged in the application.
static void Main(string[] args)
{
string database = "SqlServer";
IDataProvider dataProvider = null;
if (database.Equals("SqlServer"))
{
dataProvider = new SqlDataProvider();
}
else if(database.Equals("Oracle"))
{
dataProvider = new OracleDataProvider();
}
}