oopprinciples

Objectively Good OO Design Principles


Premise

I believe that there is a way to objectively define "Good" and "Bad" Object-Oriented design techniques and that, as a community we can determine what these are. This is an academic exercise. If done with seriousness and resolve, I believe it can be of great benefit to the community as a whole. The community will benefit by having a place we can all point to to say, "This technique is 'Good' or 'Bad' and we should or should not use it unless there are special circumstances."

Plan

For this effort, we should focus on Object-Oriented principles (as opposed to Functional, Set-based, or other type of languages).

I'm not planning on accepting one answer, instead I'd like the answers to contribute to the final collection or be a rational debate of the issues.

I realize that this may controversial, but I believe we can iron something out. There are exceptions to most every rule and I believe this is where the disagreement will fall. We should make declarations and then note relevant exceptions and objections from dissenters.

Basis

I'd like to take a stab at defining "Good" and "Bad":

Starter

As an example of what I think a good contribution would look like, I'd like to propose a "Good" principle:

Separation of Concerns

[Short description]

Example

[Code or some other type of example]

Goals

[Explanation of what problems this principle prevents]

Applicability

[Why, where, and when would I use this principle?]

Exceptions

[When wouldn't I use this principle, or where might it actually be harmful?]

Objections

[Note any dissenting opinions or objections from the community here]


Solution

  • Separation of Concerns

    Prefer Aggregation to Mixin-style Inheritance

    While functionality can be gained by inheriting from a utility class, in many cases it can all be gained using a member of said class.

    Example (Boost.Noncopyable):

    Boost.Noncopyable is a C++ class that lacks a copy constructor or assignment operator. It can be used as a base class to prevent the subclass from being copied or assigned (this is the common behavior). It can also be used as a direct member

    Convert this:

    class Foo : private boost::noncopyable { ... };
    

    To this:

    class Foo {
        ...
    private:
        boost::noncopyable noncopyable_;
    };
    

    Example (Lockable object):

    Java introduced the synchronized keyword as an idiom to allow any object to be used in a threadsafe manner. This can be mirrored in other languages to provide mutexes to arbitrary objects. A common example is data structures:

    class ThreadsafeVector<T> : public Vector<T>, public Mutex { ... };
    

    Instead, the two classes could be aggregated together.

    struct ThreadsafeVector<T> {
        Vector<T> vector;
        Mutex mutex;
    }
    

    Goals

    Inheritance is frequently abused as a code-reuse mechanism. If inheritance is used for anything besides an Is-A relationship, overall code clarity is reduced.

    With deeper chains, mixin base classes greatly increase the likelihood of a "Diamond of Death" scenario, wherein a subclass ends up inheriting multiple copies of a mixin class.

    Applicability

    Any language that supports multiple inheritance.

    Exceptions

    Any case where the mixin class provides or requires overloading members. In this case, inheritance usually implies an Is-Implemented-In-Terms-Of relationship, and an aggregate will not be sufficient.

    Objections

    The result of this transformation may lead to public members (e.g. MyThreadSafeDataStructure may have a publicly-accessible Mutex as a component).