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.

Design Guidelines - Classes& Structures

Classes are reference types and structures are value types. Reference types are allocated on the heap, and memory management is handled by the garbage collector. Value types are allocated on the stack or inline and are deallocated when they go out of scope. In general, value types are cheaper to allocate and deallocate. However, if they are used in scenarios that require a significant amount of boxing and unboxing, they perform poorly as compared to reference types.

Consider defining a structure instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.

Do not define a structure unless the type has all of the following characteristics:
It logically represents a single value, similar to primitive types (integer, double, and so on).
It has an instance size smaller than 16 bytes.
It is immutable.
It will not have to be boxed frequently.

If one or more of these conditions are not met, create a reference type instead of a structure. Failure to adhere to this guideline can negatively impact performance.

Design Guidelines - Types & Namespaces

Do use namespaces to organize types into a hierarchy of related feature areas.

Avoid very deep namespace hierarchies. Such hierarchies are difficult to browse as the user has to backtrack often.

Avoid having too many namespaces.

Types that are used in the same scenarios should be in the same namespaces when possible. Users should not have to import many namespaces to develop common scenarios.
Avoid having types designed for advanced scenarios in the same namespace as types intended for common programming tasks.
In general, you should place advanced types in a namespace within the general namespace, and use Advanced as last identifier in the name. For example, the commonly used types related to XML serialization are in the System.Xml.Serialization namespace and the advanced types are in the System.Xml.Serialization.Advanced namespace.

Do not define types without specifying their namespaces.

Types that are not assigned a namespace are placed in the global namespace. Because they are not in a feature-specific namespace, types in the global namespace are difficult to find using development tools. Additionally, name collisions in the global namespace cannot be resolved

Design Guidelines - Naming Guidelines

Capitalization Conventions:
Pascal Casing
The first letter in the identifier and the first letter of each subsequent concatenated word are capitalized. You can use Pascal case for identifiers of three or more characters. For example:
BackColor
Camel Casing
The first letter of an identifier is lowercase and the first letter of each subsequent concatenated word is capitalized. For example:
backColor
Uppercase
All letters in the identifier are capitalized. For example:
IO

Capitalization Rules for Identifiers
When an identifier consists of multiple words, do not use separators, such as underscores ("_") or hyphens ("-"), between words. Instead, use casing to indicate the beginning of each word.
The following guidelines provide the general rules for identifiers.
Do use Pascal casing for all public member, type, and namespace names consisting of multiple words.
Note that this rule does not apply to instance fields
Do use camel casing for parameter names.

Do not assume that all programming languages are case-sensitive. They are not. Names cannot differ by case alone.