Ood class – Abstract Class – Interface



Download 20.21 Kb.
Date conversion28.01.2017
Size20.21 Kb.
OOD

Class – Abstract Class – Interface




Hierarchy before OOD


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)?



Notation:

C = concrete class

A = abstract class

I = interface


A

C



C

C

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)

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).


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.

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).
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


The database is protected by copyright ©ininet.org 2016
send message

    Main page