Design: Class vs. Interface.

  1. “Is-A” vs. “Can-Do” relationship.
  2. Ease of use.
  3. Consistent implementation.
  4. Versioning.

 

1. When we use a class as a base type it’s called “Is-A” relationship, when we use an interface it’s called “Can-DO” relationship. Sometimes we are not allowed to choose between the relationships like in case with value types because they are already implicitly are inherited from the System.ValueType.

Example 1:

public abstract class Stream : MarshalByRefObject, IDisposable
{…}

 

2. Ease of use in a derived class like in case with the System.IO.Stream.

3. Consistent implementation. When we use an interface instead of a class it is easy to implement this interface incorrectly.

4. Versioning. When we use a type as a base class it is easy to add new members to the class, because you don’t have to implement all new members in the derived class like in case with interfaces.

Use an abstract class instead of interfaces to decouple the contract from implementations.

Example 2:

namespace System.Collections.Generic

{

public interface IList<T> : ICollection<T>

    {…}

public class List<T> : IList<T>, System.Collections.IList

    {…}

public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>

{…}

public class Dictionary<TKey,TValue>: IDictionary<TKey,TValue>, IDictionary, ISerializable, IDeserializationCallback 

{…}

}

Choose a “CAN-DO” relationship between the code and the interface because the implementations of those interfaces are radically different from one another.

Example 3:

namespace System.Collections.Generic {

   

    using System;

    // The generic IComparer interface implements a method that compares

    // two objects. It is used in conjunction with the Sort and

    // BinarySearch methods on the Array, List, and SortedList classes.

    public interface IComparer<T> {

        // Compares two objects. An implementation of this method must return a

        // value less than zero if x is less than y, zero if x is equal to y, or a

        // value greater than zero if x is greater than y.

        //

        int Compare(T x, T y);

    }

}

namespace System.Collections.Generic

{

    using System.Globalization;

    using System.Runtime.CompilerServices;

   

    [Serializable()]

    public abstract class Comparer<T> : IComparer, IComparer<T>

    {}

}

Finally, use both versions: define the interfaces and provide the base class which implements those interfaces.

See also: Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries. ( 4.3 Choosing Between Class and Interface p. 77)

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s