I want to make diagrams that explain how Inheritance and Polymorphism work conceptually.
Assume that we have a Cat
class that inherits from the Animal
class. The way that I can think of a Cat
object is that it is an object that contains a copy of the members of the Cat
class and also it have an Animal
object inside of it.
Note: technically, I think that all Cat
objects share the same copy of the methods of the Cat
class, however, it is clearer to conceptually think of every Cat
object as if it contains its own copy of the methods of the Cat
class.
Now when we do:
Cat cat1 = new Cat();
Then cat1
will point to the Cat
object, this is how the diagram would look like:
The operations that are allowed on the cat1
variable are the following:
cat1.x
(will access thex
variable inside theCat
object).cat1.move()
(will call themove()
method inside theCat
object).cat1.jump()
(will call thejump()
method inside theAnimal
object).
And when we do:
Animal animal1 = cat1;
Then animal1
will point to the Animal
object that is inside the Cat
object, this is how the diagram would look like:
Note: the arrow pointing at the Animal
object doesn't mean that animal1
actually point to a different location in memory than cat1
, the truth is that animal1
and cat1
point to the same location in memory, the arrow pointing at the Animal
object is just to indicate that animal1
can only access the members inside the Animal
object (unless we have an overridden method).
The operations that are allowed on the animal1
variable are the following:
animal1.x
(will access thex
variable inside theAnimal
object).animal1.move()
(now if themove()
method is not overridden in theCat
class, thenanimal1.move()
should call themove()
method inside theAnimal
object, however, since themove()
method is overridden in theCat
class, thenanimal1.move()
would call themove()
method inside theCat
object).animal1.jump()
(will call thejump()
method inside theAnimal
object).
Is my understanding correct?
2 Answers 2
I think to do this right you need to detach the method names from the object blocks. The whole point of polymorphism is the separation of the interface from the implementation. It does not help to see the method names within a particular object block, that is only confusing. The method names should be on the outside, at the outer edge of the outer block. Then some lines can route them to the inners of a particular object where the attached implementation is, without repeating the member names. The point is to show where the pipes end up.
Another thing that is (utterly) wrong in the drawings shown is that when you cast the object to Animal, the arrow goes to Animal. This is not the case, the arrow should still go to Cat! You will be talking to a Cat no matter what you cast it to. Once you understand this you really understand polymorphism and it will get a lot easier, this is what you want to make clear.
[edit]
The pictures are portraying the objects (ancestor and descendant) as separate things which gives the wrong idea. The point is they are the same, which is not conveyed by the image. Descendant does not encapsulate/contain ancestor, descendant is an extension/modification of ancestor. Once the cat object is instantiated there is no animal object.
The animal class is used as a blueprint to construct part of the cat object but the result will be a cat. Think of the real cat, does it have an animal inside of it? No. So the "animal object" box within the cat object box is inappropriate, it is all cat.
-
2agree. What diagraming paradigm is that anyway? Booch diagrams? I haven't used those for 20+ years. All the OPs representational concerns are handled in Unified Modeling Language (UML). Sequence diagrams in particular are great for illustrating object instantiation call order.radarbob– radarbob2020年07月20日 18:07:31 +00:00Commented Jul 20, 2020 at 18:07
-
1An additional misleading feature of this idea of showing a cat wrapping an animal is that it makes you think of aggregation instead of inheritance. Subclasses should not be depicted as containing the supperclass. When the time of depicting actual aggregation, how will he depict it?Tulains Córdova– Tulains Córdova2020年07月20日 18:27:22 +00:00Commented Jul 20, 2020 at 18:27
-
@radarbob That’s not booch. Boochbis recognizable among 1000 with its cloud-formed classes. That’s op freestyle for teaching purpose.Christophe– Christophe2020年07月20日 19:36:34 +00:00Commented Jul 20, 2020 at 19:36
-
@Tulains Córdova I putted the superclass object inside the subclass object to indicate that the superclass members are now part of the subclass object (for example: when we do
cat1.jump()
, we can assume that thejump()
method is part of theCat
object). However, if I would have created separate diagrams for both theCat
object and theAnimal
object, then it would not be obvious that theAnimal
members are part of theCat
object...Tom– Tom2020年07月21日 05:45:36 +00:00Commented Jul 21, 2020 at 5:45 -
@Tulains Córdova ...As for how can I depict aggregation, I can do that by putting a variable name before the member object diagram (like I did with the
x
variables), while theAnimal
superclass object diagram doesn't have a variable name before it, so this way it would be obvious to distinguish between the superclass object diagram and the member object diagram (by "member object diagram" I mean the object diagram of the object that is a member of another object).Tom– Tom2020年07月21日 05:45:43 +00:00Commented Jul 21, 2020 at 5:45
In the second case, with polymorphism, animal1.move()
will still invoke move()
of the Cat
. In this regard, the diagram is misleading.
If you want to get this right and unambiguous in a diagram, you'd have to draw some kind of redirection of the call to the right method whatever the type that is used:
- a vtable-like presentation in your embedded box diagram
- or simply lollipop connectors, to show that it's like a plug with some special wiring behind the wall.
Elaborating on what you did, I could fo example imagine something like this:
The second alternative that I mentioned, uses the lollipop connectors that you typically find in an UML component and the view of the internal subobjects like you'd fin in an UML composite structure:
I hesitated to show it, because it looks like UML, but it's not valid UML. The lollipops for example are used to show public members instead of full interfaces, and the ports are misused to route the call to the right implementation instead of component elements. In addition your typed object handles do not consume the interface but provide. On the other side, it could have advantages to familiarize your students intuitively with these modelling principles: it's a good opportunity to make seem natural what otherwise looks rather abstract.
-
Isn't that what Tom said? (In the second bullet-point, he gives the "without-override" and "with-override" scenarios: "however, since the move() method is overridden in the Cat class, then animal1.move() would call the move() method inside the Cat object") Agree the diagram is rather misleading.SusanW– SusanW2020年07月20日 15:38:21 +00:00Commented Jul 20, 2020 at 15:38
-
@SusanW I must say that reading it I'm still not sure. But in any case, the diagram is misleading. I'll edit to clarify.Christophe– Christophe2020年07月20日 15:41:12 +00:00Commented Jul 20, 2020 at 15:41
-
@Christophe "the diagram is misleading" Are you saying this because
animal1
is pointing to theAnimal
object whileanimal1.move()
is not calling themove()
method inside theAnimal
object?Tom– Tom2020年07月20日 15:51:13 +00:00Commented Jul 20, 2020 at 15:51 -
@Tom indeed. >The diagram doesn't show that another move() is called than the one that is in the box. You must show that ,even if you point at animal it's the other move that is executed. You can do this by showing some wiring. By the way, the second x is misleading: it should be like the jump: you only have one, and it's the one in animalChristophe– Christophe2020年07月20日 15:55:29 +00:00Commented Jul 20, 2020 at 15:55
-
1@Tom and yes, if the x of cat is inherited from animal, there's only one x in the Cat. I've tried to show this in an alternate diagram that show who provides what (so x is in animal and jump only in cat) but cat provides another method + its own implementation of move(). THe grey parts are accessible from outside, but the wiring makes sure that whatever object is used, the right method is called (polymorphism).Christophe– Christophe2020年07月20日 16:26:56 +00:00Commented Jul 20, 2020 at 16:26