You are here

Abstraction vs. Commonality

26 July, 2019 - 09:51
Available under Creative Commons-ShareAlike 4.0 International License. Download for free at http://cnx.org/contents/402b20ad-c01f-45f1-9743-05eadb1f710e@37.6

A subtle but extremely important point is that

Commonality does not imply abstract equivalence.

Just because a feature is common to every item in a set, does not necessarily mean that it represents some sort of abstract feature of those elements. For instance, cats, dogs, humans, and rats are all mammals where a mammal is defined as an animal that produces milk to feed its young. One could thus make a class model where a superclass Mammal has subclasses Cat, Dog, Human and Rat. One common feature is behavior is that cats, dogs, humans and rats all give live birth of their young. So it is tempting to say that the Mammal class should also embody the "live birth" behavior. However, as one wanders the world discovering new mammals, in the backwaters of Australia one finds the duck-billed platypus which produces milk and is therefore clearly a mammal. However, the duck-billed platypus also lays eggs. Thus the "live birth" behavior does not belong in the Mammal superclass as it was only a coincidence that it was common to our initial set of subclasses. More importantly, being able to give live birth was never part of the abstract definition of a mammal and thus should never have been included in the Mammal superclass in the first place.

Cats, monkeys and whales, while diverse creatures, are all mammals. Hence to model such a system in the computer, it makes sense to make Cat, Monkey and Whale all subclasses of an abstract Mammal superclass. Each species has many behaviors (methods) but I will only concentrate on 3 in particular:

  1. boolean givesMilk() : returns true if the animal can give milk to feed its young, false otherwise
  2. String makeSound() : returns a String represenation of a common sound the animal makes.
  3. boolean givesLiveBirth(): returns true if the animal bears live young.

In the table below are the methods and what happens when each species executes that method:

Mammal

Method

   
 

boolean givesMilk()

String makeSound()

boolean givesLiveBirth()

Cat

true

"Meow"

true

Monkey

true

"Screech"

true

Whale

true

"[whale song]"

true

 

We could start out with the following class implemenation:

media/image2.png
Figure 2.2 Model of Mammals 
No common methods defined in the superclass. 
Note: Italics signify abstract methods or classes
Note: return value : method name(parameter type #1 parameter name #1, parameter type #2 parameter name #2, etc)

Let's start our analysis:

  • A mammal is defined by the fact that it gives milk to feed its young. It is thus not surprising that all the givesMilk() methods in the subclasses return true. The givesMilk() method is a prime candidate for "hoisting" up into the Mammal superclass ("hoisting" moving the method upwards from the subclass to the superclass).
  • makeSound() returns a different value for each species, but intrisically, we expect any animal, which includes mammals, to be able to make some sort of sound. Thus Mammals should have a makeSound() method, but since, at the Mammals level, we don't know exactly how that sound will be made, the method at that level must be abstract. The makeSound() method at the concrete Cat, Monkey and Whale level however, would be concrete because each animal makes its own unique sound.
  • givesLiveBirth() returns exactly the same value for all of our rather diverse selection of animals here. It seems like a fne candidate for hoisting. Or is it....? Let's go ahead an hoist it anyway.

This is what we have so far:

media/image3.png
Figure 2.3 Model of Mammals 
Abstract and common methods hoisted to the superclass. 

Before we go charging ahead, let's stop for a moment and review what we've done: Cats, monkeys, and whales do represent a wide spectrum of mammals, but remember, the abstract Mammal class is a representation of ALL mammals, not just the ones we have so far. The correlation of like behavior with all our represented animals does not imply its inclusion in their abstract representation!

For instance, one day, in our wanderings through Australia, we encounter a Duckbilled Platypus. Let's see how it behaves with respect to our 3 methods:

Mammal

Method

boolean givesMilk()

String makeSound()

boolean givesLiveBirth()

Duckbilled Platypus

true

"growl"

false

 

Duckbilled platypus lay eggs!!

Giving live birth is not part of the definition of a mammal. On the other hand, the question of whether or not the animal gives live birth can always be asked of any animal, including all mammals. The result may be true or false however, so the method must be abstract at the Mammal level.

Our class structure should look like this:

media/image4.png
Figure 2.4 Model of Mammals 
Properly abstracted model. 

Hoisting does not guarantee proper abstraction. Hoisting should be driven by a need for abstraction, not by coincidence.

Another key notion that the union pattern emphasizes is levels of abstraction. What we see is that the concept of a liquid is more abstract than milk. In turn, the general concept of milk is more abstract than "2% milk" or "skim milk" which would be subclasses of milk. In general we can see that a superclass is a distinctly higher level of abstraction than any of its subclasses. One of the key tools we use to help us design and build high quality object-oriented systems is careful attention to the abstraction level at any given moment.

Good OOP code always maintains a consistent level of abstraction.

Abstraction levels are links in a chain. A chain is only as strong as its weakest link. A program is only as abstract as its lowest abstraction level.

Levels of abstraction illustrate another important aspect of an OO program. Since a superclass represents the union of the subclasses or conversely, that the superclass can be represented by any of its subclasses, we see that the superclass is an embodiment of all the invariant aspects of the subclasses. That is, the superclass's definition is all that is abstractly equivalent about the subclasses-all that does not change from subclass to subclass. Note that this does not imply that the values of common fields are necessarily the same, just that, perhaps, that the field exists. Not does it imply that what is common to all the subclasses is necessarily what is abstractly equivalent about them (see the note above). The differences between the subclasses is what creates the variations in how the program behaves when any given subclass is used in place of the superclass. We call this the variant aspects of the system.

The total behavior of a program is the combination of its variant and invariant behaviors.