Classes - Abstract Classes - Interfaces (OOD)

advertisement
OOD
Class – Abstract Class – Interface
Hierarchy before OOD
Dog
Cat
Dolphin
name
name
name
getName()
getName()
getName()
To see the problem with this type of object organization (and the reason why companies can’t afford this type of organization), imagine
yourself giving (actually envision yourself doing the coding here; it’ll help) each of these objects hair (attribute) and liveBirth() (behavior).
What will you find yourself doing? What’s the problem(s)? Yep, you might end up doing a lot of copying/pasting/editing of the same code over
and over into the three classes.
Solution:
Pull out the common stuff (i.e., attributes/behaviors) and put into one place (i.e., into an abstract class). Now, you’ve gained
abstraction, localization, and code reuse – three of the most important benefits of OOD. Can you explain why you’ve gained
abstraction, localization, and code reuse (i.e., what do those vocabulary words mean)?
Mammal
A
name
getName()
Dog
C
created by Mr. Wittry – 8/12/2003
Cat
C
Dolphin
C
Notation:
C = concrete class
A = abstract class
I = interface
Let’s take a short breather (an aside) and talk about the differences between a class, an abstract class, and an interface – from the viewpoint of
what the Java language expects (let’s leave the difficult concept stuff aside for a second).
Classes (concrete)
- contain data (attributes – usually nouns)
- contain method implementations (behaviors – usually verbs)
- objects of this concrete type can be instantiated (e.g., Cat garfield = new Cat();)
- reference variables of this type can be used (e.g., Cat garfield;)
Abstract Classes
- contain data
- contain method implementations
- contain abstract methods (i.e., methods with no implementation)
- objects of this abstract type can not be instantiated
- reference variables of this type can be used (e.g., Mammal someMammal;)
- reference variables of this type can also be used to refer to objects which
are subclasses (e.g., Mammal someMammal = new Cat();)
Interfaces
- contain abstract methods
- do not contain data (other than constants if you wish)
- reference variables of this type can be used (e.g., Locatable loc;)
- reference variables of this type can also be used to refer to objects which
are subclasses (e.g., Locatable loc = new Dolphin();) (see picture below to see why this is possible)
created by Mr. Wittry – 8/12/2003
Ok, back to OOD.
So now let’s add hair and liveBirth(). Where do they go? Yep, into Mammal. Why?
Now,
getName() is probably implemented within Mammal because it just returns the name – each subclass doesn’t need to
getName() in a different manner.
What about liveBirth()? Let’s pretend that each class (dog/cat/dolphin) has a liveBirth() in a little different way but that
each class definitely has a liveBirth(). In that case, we’d want each class to write their own implementation of
liveBirth() – and ensure that they do so - so, we make the method abstract (no implementation) and put it into Mammal.
Great, we’re doing well now. We understand why we have abstract classes and concrete classes.
So what are interfaces for? To see that, let’s now pretend we want to be able to display these concrete objects. In order to do that, a common
thing to do would be to have a display class which would need to know each objects “position” within display/environment. (Hmmmm, sound
familiar? Well, I chose this example because it mimics what’s going on in the MBS.) So, we’d like to guarantee that each object has a way to
allow access to its location. In addition, we’re not interested in all of our concrete classes having a location and being used in the MBS – just
Dolphin (the one mammal in this example that can swim – don’t give me a hard time here!). So, let’s create the picture below:
Aside: in general, when a class or abstract class has
data (e.g., hair), there should be an accessor and a
mutator (e.g., getHair() & setHair()) if the subclasses
or clients are going to need to get at the private data
(please don’t use ‘protected’ data if you know what that is – it
breaks the idea of encapsulation and provides an unsafe means
of manipulation – in addition to the fact that it is a broken
feature in Java anyway).
created by Mr. Wittry – 8/12/2003
Mammal
Locatable
A
I
name
getName()
Dog
C
Cat
C
location()
Dolphin
C
Alligator
C
So, if Locatable is an abstract class, then we have a Java language-restriction problem. Java does not allow us to inherit from more than one
class/abstract class (called “multiple inheritance”). So, its way around that restriction is to allow us to “inherit” from as many interfaces as we
like. We’re not really inheriting anything (after all, interfaces don’t have data nor implementations to inherit). But the interface ensures that
‘subclasses’ of it all have a minimum common look (they must implement the methods within the interface) and, in addition, the interface will
act as a ‘superclass’ with respect to using it as a reference type (very useful for polymorphism(foreshadowing)).
Back to our new drawing: instead of making Locatable an abstract class, we’ll make it an interface. To see if you understand yet, let’s classify
dogs and cats as pets. Draw in where “Pet” goes and whether it is a class, an abstract class, or an interface. Justify your choice.
Did you make “Pet” an interface with subclasses of Dog and Cat? Perrrrrfect. Did you make it concrete or abstract? If so, what’s the problem
with that? Maybe you put Pet beneath Mammal and above the Dog/Cat/Dolphin. What would that then mean? Yep, conceptually it would mean
that all pets are mammals (does anyone have a pet snake?). So, we probably wouldn’t like that design.
created by Mr. Wittry – 8/12/2003
So, let’s try to handle one more important term: polymorphism. Let’s add one more method to the abstract class Mammal – move().
First, do you think move() should be abstract (implemented in each subclass) or should it be implemented in Mammal? The answer to that has
to do with whether you believe all subclasses of Mammal would “move” in the same manner. Since, more than likely, the answer is no, then
the method should be abstract and we’ll let each subclass determine what “move” means to it.
Next, as an example, it is often useful to be able to hold a group of objects(Dogs, Cats, Dolphins), each of which is related through a common
superclass (Mammal). Here is some sample code:
Mammal[] bunchOfMammals = new Mammal[10]; // (no object instantiated here – only 10 references allocated)
bunchOfMammals[0] = new Dog();
// (now we’re instantiating an object)
bunchOfMammals[1] = new Cat();
bunchOfMammals[ ] = …
bunchOfMammals[ ] = …
etc.
Now, we’d like to “move” each of the mammals. To make the point hit home even more, let’s say that the vector has been scrambled in the
mean time and we don’t know at run time where the Dogs, Cats, and Dolphins are within the vector. No problem – polymorphism to the rescue.
for (Mammal mam: bunchOfMammals)
mam.move();
When the reference bunchOfMammals[i] refers to a Dog at run time, the binding of a specific method to a specific object will occur and the
move() in the Dog class will be called – likewise, if bunchOfMammals[i] happens to refer to a Cat or a Dolphin. The reason this compiled
fine, is that at compile time the method move() was found in the array’s type (i.e., Mammal). The compiler knows that at some point during
runtime it will be able to find an implementation somewhere in the inheritance structure beginning with the actual object being referenced (if it
doesn’t find it in that object, it moves its way up the inheritance structure until it finds an implementation – because, after all, it compiled
because some class somewhere along the line below Mammal implemented the interface’s move() – it’s a requirement). Pretty cool.
By the way, polymorphism is often referred to as either “late binding” or “dynamic binding” – or “belated binding”(just kidding on that one).
created by Mr. Wittry – 8/12/2003
Another common thing to do in Java is to use an Iterator object to iterate through a collection. Example:
ArrayList list = new ArrayList();
list.add(new Dog());
list.add(new Cat());
… // add more Dogs, Cats, Dolphins
for (Iterator iter = list.iterator(); iter.hasNext(); )
( (Mammal)iter.next() ).move();
// polymorphic move()
(note for code segment above: I wouldn’t use this option in Java 5 – just showing you how to use an iterator. I would use the “for each…” loop
demonstrated on the previous page, of course!)
Ok, one last question for you. Given the statement:
System.out.println(someObj);
explain what is known at compile time and why it compiles, what is being decided at run-time, and what specifically is being invoked – in your
description, use the terminology.
created by Mr. Wittry – 8/12/2003
Download