Monday, May 4, 2009

Design Guidelines - Interface Design

An interface defines the signatures for a set of members that implementers must provide. Interfaces cannot provide implementation details for the members. For example, the ICollection interface defines members related to working with collections. Every concrete class that implements the interface must supply the implementation details for theses members. While classes can inherit only from a single class, they can implement multiple interfaces. The following guidelines help ensure that your interfaces are correctly designed.

Do define an interface if you need some common functionality to be supported by a set of types that includes some value types.

Value types must inherit from ValueType. For this reason, abstract classes cannot be used to specify a contract for value types; interfaces must be used instead.

Consider defining an interface if you need to support its functionality on types that already inherit from some other type.

Avoid using marker interfaces (interfaces with no members).

Do provide at least one type that is an implementation of an interface.

This helps ensure that the interface is well designed and can be implemented without too much difficulty. The Int32 class provides an implementation for the IComparable interface.

Do provide at least one member that consumes each interface you define (for example, a method that takes the interface as a parameter or a property typed as the interface).

This is another mechanism that helps ensure that the interface is well designed and can be used without too much difficulty.

Do not add members to an interface that has previously shipped.

Adding new members will break code that implemented the previous version of the interface. This is one of the main reasons why, in general and where possible, classes are preferable to interfaces.
If the shipping definition of the interface requires additional members, you can implement a new interface and the appropriate members to consume it.

Design Guidelines - Static class design

Static classes are classes that do not contain instance members other than those inherited from Object, and do not have a callable constructor. The following guidelines help ensure that your static classes are correctly designed.

Do use static classes sparingly.

Static classes should be used only as supporting classes for the object-oriented core of the framework.

Do not treat static classes as a miscellaneous bucket.

The Environment class is a good example of the proper use of a static class. This class provides access to information about the current user environment.

Do not declare or override instance members in static classes.

If the design of a class suggests that there should be instance members, the class should not be marked static.

Do declare static classes as sealed and abstract, and add a private instance constructor, if your programming language does not have built-in support for static classes.

Design Guidelines - Abstract class design

Because abstract classes should never be instantiated, it is important to correctly define their constructors. It is also important to ensure that the functionality of your abstract class is correct and easily extended. The following guidelines help ensure that your abstract classes are correctly designed and work as expected when implemented.

Do not define public or protected internal (Protected Friend in Visual Basic) constructors in abstract types.

Constructors with public or protected internal visibility are for types that can be instantiated. Abstract types can never be instantiated.

Do define a protected or an internal constructor in abstract classes.

If you define a protected constructor in an abstract class, the base class can perform initialization tasks when instances of a derived class are created. An internal constructor prevents the abstract class from being used as the base class of types that are not in the same assembly as the abstract class.

Do provide at least one concrete type that inherits from each abstract class that
you ship.

This practice helps library designers locate problems or oversights in the abstract class design. It also means that for high-level scenarios where developers might not understand abstract classes and inheritance, they can use the concrete class without having to learn these concepts. For example, the .NET Framework provides the abstract classes WebRequest and WebResponse to handle sending requests to, and receiving replies from a Uniform Resource Identifier. As one of several concrete implementations for these abstract classes, the Framework includes the HttpWebRequest and HttpWebResponse classes, which are HTTP-specific implementations of the abstract classes.

Design Guidelines - Classes & Interfaces

An interface defines the signatures for a set of members that implementers must provide. Interfaces cannot provide implementation details for the members. For example, the ICollection interface defines members related to working with collections. Every class that implements the interface must supply the implementation details for theses members. Classes can implement multiple interfaces.

Classes define both member signatures and implementation details for each member. Abstract (MustInherit in Visual Basic) classes can behave like interfaces or regular classes in that they can define members, and they can provide implementation details but are not required to do so. If an abstract class does not provide implementation details, concrete classes that inherit from the abstract class are required to provide the implementation.

While both abstract classes and interfaces support separating contract from implementation, interfaces cannot specify new members in later versions while abstract classes can add members as needed to support additional functionality.

Do favor defining classes over interfaces.

In later versions of your library, you can safely add new members to classes; you cannot add members to interfaces without breaking existing code.

Do use abstract (MustInherit in Visual Basic) classes instead of interfaces to decouple the contract from implementations.

Do define an interface if you need to provide a polymorphic hierarchy of value types.

Value types must inherit from ValueType, and can inherit only from ValueType, so they cannot use classes to separate contract and implementation. In this case, you must use an interface if your value types require polymorphic behavior.

Consider defining interfaces to achieve an effect similar to that of multiple inheritance.

If a type must implement multiple contracts, or the contract is applicable to a wide variety of types, use an interface. For example, IDisposable is implemented by types used in many different scenarios. Requiring classes to inherit from a base class to be disposable would make the class hierarchy too inflexible. Classes such as MemoryStream, which should inherit their stream-based contracts from their parent classes, would not be able to do so and also be disposable.