Source

IS prolific use of inheritance a sign of a poorly design program?

A principle that is used to validate inheritance is Liskov Substitution principle (LSP).  Basically, it implies that a subtype must be interchangeable with its super-type without adverse side effect.

With this principle in mind it’s easy to discount many particular sub/super-type inheritances.  The quintessential Uncle Bob example of a Liskov Substitution violation are Rectangle and Square.  In geometry a Square is a type of Rectangle (and Rectangle is a type of polygon, etc…); but that maxim doesn’t hold true in most OO implementations in light of LSP.

Let’s say we implement a Rectangle class like this:

    public class Rectangle {

        private int width;

        private int height;

 

        public virtual int Width {

            get { return width; }

            set { width = value; }

        }

 

        public virtual int Height {

            get { return height; }

            set { height = value; }

        }

    }

 And we wrote unit tests like this:

    [TestFixture]

    public class RectangleTests {

        [Test]

        public void WhenWidthChanged_EnsureHeightUnchanged() {

            Rectangle rectangle = new Rectangle(10, 20);

            rectangle.Width = 30;

            Assert.AreEqual(rectangle.Height, 20);

        }

    }

Now, we know that a square is a specialized rectangle whose width and height are equal.  So, we’d be tempted to write Square like this:

    public class Square : Rectangle {

        public override int Height {

            get {

                return base.Height;

            }

            set {

                base.Height = value;

                base.Width = value;

            }

        }

        public override int Width {

            get {

                return base.Width;

            }

            set {

                base.Width = value;

                base.Height = value;

            }

        }

    }

 If we no substituted a Square object for the Rectangle object in the WhenWidthChanged_EnsureHeightUnchanged test (e.g. we registered Square for type Rectangle in our IoC) we’d get a failure.

In the same grain as Liskov, I believe that every non-static super-type should be used within the code base–meaning not only should a subtype be substitutable for a super-type but that subtype must be substituted at least somewhere in the code base.

I call this the Tangible Super-type principle.  Like a corollary to LSP, it states that every super type must be substituted by one or more subtypes.

This means I believe inheritance should be used for IS-A relationships.  It also means I don’t believe inheritance is a means of sharing code.  I would suggest using something like the Service Pattern instead of “abstract” super-types.  And yes, this means I think there are better alternatives to the Layer Super-type pattern.

with : Uncategorized