.NET Interface-based Programming
In component-based programming, the basic unit of use in an application is a binary-compatible interface. The interface provides an abstract service definition between the client and the object. This is in contrast to the object-oriented view of the world that places the object implementing the interface at the center. An interface is a logical grouping of method definitions that acts as the contract between the client and the service provider. Each provider is free to provide its own interpretation of the interface and its own implementation. To use a component, the client only needs to know the interface definition and have a binary component that implements that interface. This extra level of indirection between the client and the object provides for interchangeability between different implementations of the same interface, without affecting client code.
Separation of interface from implementation is a core principle of component-oriented programming.
In traditional object-oriented programming, when developers wanted to define a service abstraction, they used an abstract class. The abstract class served to define a set of signatures that multiple classes would implement after deriving from the abstract class. By having a common base class to different service providers, they all became polymorphic with that service abstraction, and the client could potentially switch between providers with minimum changes. In .NET, you can still use abstract classes, but .NET also defines interfaces. The reserved word, interface, defines a CLR reference type that cannot have any implementation, cannot be instantiated, and has only public members. Saying that an interface cannot have implementation means that all interface methods and properties are abstract. Saying it cannot be instantiated means that the interface is an abstract class. For example, this interface definition: public interface IMyInterface
is almost equivalent to this class definition:public abstract class MyInterface
abstract public void Method1();
abstract public void Method2();
abstract public void Method3();
There are a few important differences between a pure abstract class (MustInherit in Visual Basic .NET) and an interface:
- An abstract class can still have implementation. It can have member variables and it can have non-abstract methods or properties. An interface, on the other hand, cannot have implementation or member variables.
- A .NET class can derive from only one base class, even if that base class is abstract. A .NET interface can derive from as many interfaces as required.
- An abstract class can derive from any other class or interface(s). An interface can derive only from other interfaces.
- An abstract class can have non-public (protected or private) methods and properties, even if they are all abstract. In an interface, by definition, all members are public.
- An abstract class can have static methods and static members. An abstract class can define constants. An interface can have none of those.
- An abstract class can have constructors. An interface cannot.
These differences are deliberate, not to restrict interfaces, but to provide for a formal public contract between a service provider (the classes implementing the interface) and the service consumer (the class's client). By disallowing any kind of implementation detail in interfaces (such as method implementation, constants, static members, and constructors), .NET promotes loose coupling between the service providers and the client. Because there is nothing in the contract that even hints at implementation, then by definition, the implementation is well encapsulated behind the interface, and the service providers are free to change their implementation without affecting the client.
You can even say that the interface acts like a binary shield, isolating both parties from each other. Because interfaces cannot be instantiated, .NET forces clients to choose a particular implementation to instantiate. Having only public members in an interface complements the contract semantics nicely: you would not want a contract with hidden clauses or "fine print." Everything the contract implies should be public and well defined. The more explicit and well defined a contract is, the less likely it is that there will be conflicts down the road regarding what exactly the class providing the service is required to do. The class implementing the interface must implement all the interface members without exception, because it is committing to provide this exact service definition.
An interface can only extend other interfaces, not classes. By deriving a new interface from an existing interface, you define a new and specialized contract, and the class implementing an interface must implement all members of the base interface(s). A class can choose to implement multiple interfaces, just as a person can choose to commit to multiple contracts.
Treating interfaces as binary contracts and shielding clients from changes made to the service providers is exactly the idea behind COM interfaces and, logically, .NET interfaces have the same semantics as COM interfaces. If you are an experienced COM developer or COM architect, then working with interfaces is second nature to you, and you will feel right at home with .NET interfaces.
By: Juval Lowy
Juval Löwy is a software architect and the principal of IDesign, a consulting and training company focused on .NET architecture consulting and advanced .NET training. This article contains excerpts from his latest book (Programming .NET Components 2nd Edition (O'Reilly, 2005). Juval is a frequent presenter at development conferences and Microsoft's Regional Director for the Silicon Valley.
Over the last three years Juval has been part of the Strategic Design Review process for .NET 2.0.
Microsoft recognized Juval as a Software Legend as one of the world's top .NET experts and industry leaders.
Contact him at www.idesign.net
.NET is not just object-oriented; it is fundamentally component-oriented. A prime principle of component-oriented programming is interface-based programming. The interface provides an abstract service contract between the client and the object. Because there is nothing in the contract that even hints at implementation, then by definition, the implementation is well encapsulated behind the interface and the service providers are free to change their implementation without affecting the client.