interface segregation main purpose and uses of interface c#
interface segregation main purpose and uses of interface c#
According to Wikipedia the interface segregation principle (ISP) states that no client should be forced to depend on methods it does not use. For the sake of this post I will use the term “server” to describe a class in an API – a business object for example. “Client” will be used to describe the consumer of a service – it could be a user interface component for example. The client has a dependency on a server at some level, the server knows nothing about a client.
As an example, let’s assume a team responsible for creating the server was tasked with creating a basic email object. Being good programmers, they designed to an interface and came up with the following Interface and implementation.
public interface IMessage
{
IList<String> ToAddresses { get; set; }
string MessageBody { get; set; }
string Subject { get; set; }
bool Send();
}
public class SmtpMessage:IMessage
{
public IList<String> ToAddresses { get; set; }
public string MessageBody { get; set; }
public string Subject { get; set; }
public bool Send()
{
//Do the real work here
return true;
}
}
Client Team A, responsible for creating an application that sends an email reminder as a part of their process, creates a client that consumes the server, and everything works. Now assume the server team gets a new requirement, from Client Team B, and now they need to support sending a text message. That’s almost the same as sending an email message so they decide to be polymorphic and use the IMessage interface.
public class SmsMessage : IMessage
{
public IList<String> ToAddresses { get; set; }
public string MessageBody { get; set; }
public bool Send()
{
//Do the real work here
return true;
}
public string Subject
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
}
Client Team B is happy, things work great and Client Team A is happy because now they can send SMS messages if they want to. Client Team A wants to enhance their system to allow for a list of addresses to be included as a blind copy, so they give the Server team this new requirement. The server team comes up with the following change to the interface and new implementations:
public interface IMessage
{
IList<String> ToAddresses { get; set; }
string MessageBody { get; set; }
string Subject { get; set; }
bool Send();
IList<String> BccAddresses { get; set; }
}
public class SmtpMessage:IMessage
{
public IList<String> ToAddresses { get; set; }
public IList<String> BccAddresses { get; set; }
public string MessageBody { get; set; }
public string Subject { get; set; }
public bool Send()
{
//Do the real work here
return true;
}
}
public class SmsMessage : IMessage
{
public IList<String> ToAddresses { get; set; }
public string MessageBody { get; set; }
public bool Send()
{
//Do the real work here
return true;
}
public string Subject
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public IList<string> BccAddreses
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
}
Client Team A gets the new server and things are great, but Client Team B is now broken and must recompile and go through a round of regression testing because a feature was added for another team.
The main goal of the interface segregation principle is to loosen the coupling between servers and their clients. An SMS message supports a list of addresses, a message body, and the notion of sending but Client B was unnecessarily also coupled to the Subject and eventually the BccAddressList property. In short, a change to Any unused property or method or a change to an overly broad interface itself will cause Team B problems. Let’s rework things a little bit and see if we can improve the design.
public interface IMessage
{
IList<String> ToAddresses { get; set; }
string MessageBody { get; set; }
bool Send();
}
public interface IEmailMessage:IMessage
{
string Subject { get; set; }
IList<String> BccAddresses { get; set; }
}
public class SmtpMessage : IEmailMessage
{
public IList<String> ToAddresses { get; set; }
public IList<String> BccAddresses { get; set; }
public string MessageBody { get; set; }
public string Subject { get; set; }
public bool Send()
{
//Do the real work here
return true;
}
}
public class SmsMessage : IMessage
{
public IList<String> ToAddresses { get; set; }
public string MessageBody { get; set; }
public bool Send()
{
//Do the real work here
return true;
}
}
With this design we have a nice crisp IMessage definition that will serve Client B very well in that the client does not depend on any methods it does not use. We have created a “wider” interface Called IEmailMessage that inherits from IMessage for Team A to use so Team A can send Email or an SMS text message.
If we want to remove the requirement that a Client understand it needs to set the address list and message body before calling the send method, we could also do this:
public interface IMessage
{
bool Send(IList<String> toAddresses, string messageBody);
}
public interface IEmailMessage:IMessage
{
string Subject { get; set; }
IList<String> BccAddresses { get; set; }
}
public class SmtpMessage : IEmailMessage
{
public IList<String> BccAddresses { get; set; }
public string Subject { get; set; }
public bool Send(IList<String> toAddresses, string messageBody)
{
//Do the real work here
return true;
}
}
public class SmsMessage : IMessage
{
public bool Send(IList<String> toAddresses, string messageBody)
{
//Do the real work here
return true;
}
}