The first mistake was imagining that because the objects had some common attributes, they were related and should share an inheritance tree. Unfortunately, this is another case where software is not real life. Just because we put entities in the same box in reality, it doesn't necessarily follow that the objects representing them in a computer system share a common hierarchy.
The OO design principle that covers this is the Liskov Substitution Principle (LSP), which Bob Martin expounded in his programmer's notebook and also his book Agile Software Development. It states, "Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it."
The example he gives is that of the square and the rectangle. In geometry, they are both two dimensional figures with four sides and four 90 degrees angles and square is a just a type of rectangle in which the height and width are equal. Obviously, it makes sense for square to derive from rectangle. However, under certain circumstances, squares and rectangles exhibit different behaviour. Change the width of a square, and its height changes too; otherwise it is no longer a square. Clearly any method that changed the width of the rectangle base class would also need to know about the special case of the square, thus breaking the LSP. The breakage tells us that, in this context, the square should most definitely not be derived from the rectangle.
Bob's point is that we naturally classify things using the ISA relationship, i.e. a square 'is a' rectangle but we should be classifying them using the 'behaves as a' relationship. Does a square behave as a rectangle? Well, as we've already seen, there are circumstances when it doesn't.
The second error is imagining we can predict the behaviour of objects before we build them. It's a bit like building a picture frame without measuring the picture first. We merrily set about developing a framework to manage our family of objects having arbitrarily decided they would behave in the same way. It's only after we finish the framework and start coding the objects themselves that we realise this is not the case. This means that every time we add a new object, we have to go back and change the framework and sometimes change all the other objects too, in order to accommodate the changes to the framework.
The third, and greatest error, is that as soon as the word framework arrives, the concept of business value disappears. The focus of development often becomes how to make the framework as flexible, extensible and future-proof as possible rather than how to deliver the customers' needs as quickly as possible. The 'F' stands for forgotten customer.
Don't get me wrong, I don't hate frameworks. There are many that I use on a daily basis and I have worked on many good ones myself. Indeed, most systems and applications contain frameworks of one sort or another. I would just like to remind you all of the three major errors listed on this page:
• Customers very rarely ask for, or are interested in frameworks; most customers want demonstrably working functionality.
• It is difficult, if not impossible, to predict the behaviour of objects in an environment before constructing and using them. It is far better to build and test the external behaviour of your objects before trying to identify their commonalities and building a framework to manage them. Automated refactoring makes this a relatively straightforward task in most environments.
• And, above all, don't be fooled by thinking that just because items share attributes, they will necessarily share behaviour.
Anyway, I know what I think the 'F' in framework stands for! ®
Buy your binary t-shirt at Cash & Carrion.