Patrice said:
"Abstract classes should be used primarily for objects that are closely
related, whereas interfaces are best suited for providing common
functionality to unrelated classes. "
Abstract classes should (in my opinion) only be used as
"implementation-helpers" skeletons of implementations which are just
missing some important parts.
Interfaces should be used to expose behavioural traits.
To use an abstract class to expose behavioural traits restricts the
implementations of that interface.
You can argue, that using an abstract class reduces the amount of
classes defined, but you save at-most 1 interface-declaration.
see the Stream class (that is abstract, all streams having the same
behavior) and the IEnumerable interface (that is implemented by classes
having nothing else in common).
I don't think Stream is a good example. A stream is a concept, an
idea... and not related to some specific way of implementation.
I would much have preferred Stream as an interface. and:
- a helper-class: ImpotentStream that would return false on all "CanX"
and throw on all other methods, allowing you to easily override just the
bits you use.
- an abstract helper-class: BlockingStream that required you to
implement CanRead, CanWrite, Read, Write, Flush & Close
- another abstract helper-class: NonBlockingStream that required you
to implement CanRead, CanWrite, BeginRead, BeginWrite & GetWaitHandle
- Adapters, implementing blocking IO in terms of non-blocking and the
inverse.
This increases the number of classes, but vastly decreses the complexity
of a working Stream-implementation.
The whole Stream "interface" smells bad, it smells like a .NET wrapper
for whatever C code was in win32.
It's basically a union of handy operations on "things". What is
"SetLength" doing on a stream? Why is CreateWaitHandle not just
"WaitHandle { get; }" and *why* is it protected. Why are there
operations to read/write single bytes.