Interfaces are a mecanism to reduce coupling between different disparate parts of a system.
Coming from a .net perspective
- the interface definition is a list of operations and/or properties
- interface methods are always public
- the interface itself doesn't have to be public
When you create a class that implements the interface, you must provide either an explicit or implicit implementation of the methods in the interface.
We also must realize that .net has only single inheritance, so interfaces are a necessity for an object to expose methods to other objects that aren't aware of it's class hierarchy. Sometimes we call this exposing behaviors.
An example that's a little more concrete:
Consider is we have many DTO's (data transfer objects) that have properties for who updated last, and when that was. The problem is that not all the DTO's have this property because it's not always relevant. At the same time we wanted a generic mechanism to guarantee these properties are set if available when submitted to the workflow, but the workflow object should be loosely coupled from the submitted objects. i.e. the submit workflow method shouldn't really know about all the subtleties of each object, and all objects in the workflow aren't necessarily DTO objects.
// first pass - not maintainable
void SubmitToWorkflow(object o, User u)
{
if( o is MapDTO )
{
MapDTO map = (MapDTO)o;
map.LastUpdated = DateTime.UtcNow;
map.UpdatedByUser = u.UserID;
}
else if( o is PersonDTO )
{
PersonDTO person = (PersonDTO)o;
person .LastUpdated = DateTime.Now; // whoops .. should be UtcNow
person .UpdatedByUser = u.UserID;
}
// whoa - very unmaintainable.
So SubmitToWorkflow must know about each and every object. Additionally, the code is a mess with one massive if/else/switch and is violating the Don't Repeat Yourself (DRY) principle.
// second pass - brittle
void SubmitToWorkflow(object o, User u)
{
if( o is DTOBase )
{
DTOBase dto = (DTOBase)o;
dto.LastUpdated = DateTime.UtcNow;
dto.UpdatedByUser = u.UserID;
}
Slightly better, but still brittle. If we want to submit other type of objects, we need more case statements. etc.
// third pass pass - also brittle
void SubmitToWorkflow(DTOBase dto, User u)
{
dto.LastUpdated = DateTime.UtcNow;
dto.UpdatedByUser = u.UserID;
Still brittle, and both methods impose the constraint that all the DTO's have to implement this property which we indicated wasn't universally applicable. We could combat this by writing do-nothing methods, but that smells bad. We don't want classes pretending they support update tracking but don't.
Interfaces to the rescue.
If we define a very simple interface
public interface IUpdateTracked
{
DateTime LastUpdated { get; set; }
int UpdatedByUser { get; set; }
}
now any class can implement the interface.
public class SomeDTO : IUpdateTracked
{
// IUpdateTracked implementation as well as other methods for SomeDTO
}
Now lets change our method to be far more generic, smaller, and more maintainable, and will continue to work no matter how many classes implement the interface (DTO's or otherwise)
void SubmitToWorkflow(object o, User u)
{
IUpdateTracked updateTracked = o as IUpdateTracked;
if( updateTracked != null )
{
updateTracked.LastUpdated = DateTime.UtcNow;
updateTracked.UpdatedByUser = u.UserID;
}
// ...
- I'll note the variation
void SubmitToWorkflow(IUpdateTracked updateTracked, User u)
which would guarantee type safety - the compiler enforces all objects are implicitly convertable to IUpdateTracked, however it doesn't seem as relevant in these circumstances, so let's not get sidetracked. See below for an example that uses interfaces in the method declaration.
In our case we have code generation and it creates these DTO classes from the database definition. The only thing we have to do is create the field name correctly and decorate the class with the interface. We don't even have to implement the properties because the compiler just makes sure that there are properties called LastUpdated and UpdatedByUser.
Maybe you're asking What happens if my database is legacy and that's not possible? You just have to do a little more typing, but another great feature of interfaces is they allow you to create a bridge between the classes. We have LegacyDTO
which is an older object having similarly-named fields. We implement the IUpdateTracked interface to provide a bridge to updating different-named properties.
public class LegacyDTO : IUpdateTracked
{
public int LegacyUserID { get; set; }
public DateTime LastSaved { get; set; }
public int UpdatedByUser
{
get { return LegacyUserID; }
set { LegacyUserID = value; }
}
public DateTime LastUpdated
{
get { return LastSaved; }
set { LastSaved = value; }
}
}
You might thing Cool, but isn't it confusing having multiple properties? or What happens if there are already those properties but they mean something else? In .net we have the ability to explicitly implement the interface. What this means is that the IUpdateTracked properties will only be visible when we're using a reference to IUpdateTracked.
public class YetAnotherObject : IUpdatable
{
int IUpdatable.UpdatedByUser
{ ... }
DateTime IUpdatable.LastUpdated
{ ... }
So you can do all sorts of things that decouple the object from methods that consume it. Interfaces are a great way to break the coupling.
There is a lot more to interfaces than just this. This is a real-life example that utilizes one aspect of interface based programming. If I get around to it, I'll try to follow up with Dependency Injection (DI) and more examples.
As I mentioned earlier, and by other responders, you can create methods that take and/or return an interface references rather than a class references. If I needed to find duplicates in a list, I could write a method that takes and returns an IList
(an interface defining operations that work on lists) and I'm not constrained to a concrete class.
// decouples the caller and the code as both operate only on IList, and are free to change the concrete objects.
public IList FindDuplicates( IList list )
{
ArrayList arrayList = new ArrayList();
// insert code to detect duplicated;
return arrayList;
}
Versioning caveat
If it's a public interface you are saying I guarantee interface x looks like this! and once you have published the interface, you should never change it. Once consumers start to rely on that interface, you don't want to break their code in the field.
Interfaces versus abstract (base) classes
Abstract classes can provide implementation whereas Interfaces cannot. Abstract classes are in some ways more flexable in the versioning aspect if you follow some guidelines like the NVI (Non-Virtual Interface) pattern.
As mentioned before (in .net) a class can only inherit from a single class, but a class can implement as many interfaces as it likes.