The classical view of inheritance in object-oriented programming is that it is a hierarchical structuring mechanism intended for conceptual specialization.
- The language should provide no more than one way of expressing any action in the language, and each construct should be backed up by solid conceptual and practical reasons.
- Which is usually considered the most important abstraction principle, is grouping like things together into classes or categories over which uniform conditions hold. It is an intensional abstraction principle that ought to be based ideally on such properties of substances that do not change in the course of time. The reverse operation of classification is instantiation.
- Classification suppresses details of instances and emphasizes properties of a class as a whole.
- Aggregation / Decomposition
- Using smaller parts in whole. Object-oriented languages typically support aggregation/decomposition by allowing the use of objects as variables in other objects. Aggregation suppresses details of components and emphasizes details of the relationship as a whole.
- The third major abstraction principle, generalization, refers to the construction of concepts that cover a number of more special concepts sharing some similarities. The converse operation of generalization is specialization.
- Generalization suppresses the differences between categories and emphasizes common properties.
- Frequently in conceptual modeling it becomes necessary to group objects together not because they have the same properties (classification), but because it is important to describe properties of a group of objects as a whole. Object-oriented programming supports grouping by allowing the definition of arbitrary collection classes such as lists, sets, bags and dictionaries. The opposite of grouping is individualization, which yields individual members of a collection.
- Grouping suppresses details of a group of objects and emphasizes the grouping of those objects together.
Subtyping vs. Subclassing
This discussion is all about respecting Liskov Substitution Principle for subtypes.
- Liskov Substitution Principle
- Liskov’s notion of a behavioral subtype defines a notion of substitutability for mutable objects; that is, if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).
- A subclass is simply a class formed using inheritance.
- A subtype is is class that can be substituted (using polymorphism) with no observable effect.
- Not all subclasses are subtypes.
The typical example of the difference in this terms is using Ellipse class for defining Circle. You cannot substitute the circle for the ellipse with no observable effects.
circle.X = 10; circle.Y = 20; // Depends on implementation, but X and Y should not differ from each other if type is subtype of ellipse. // For circle, it doesn't make any sense to have X different from Y. // Therefore one of these Asserts should fail. Assert(circle.X, 10); Assert(circle.Y, 20);
- One class is a subclass of another.
- In other words, “D is a B” usually means that concept D is a specialization of concept B, and concept B is a generalization of concept D. For instance, a “fruit” is a generalization of “apple”, “orange”, “mango” and many others. One can say that an apple is a fruit.
- Antero Taivalsaari, On the Notion of Inheritance. 1996, ACM Computing Surveys.