× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.


  • Subject: Re: Understanding Implements
  • From: "Simon Coulter" <shc@xxxxxxxxxxxxxxxxx>
  • Date: Thu, 22 Mar 01 19:32:28 +1100


Hello Buck,

You wrote:
>I'm working toward the "ah-HA!" here, and I deeply appreciate the 
>discussion to date...

>I'm missing the advantage of the extra layer of abstraction involved in
>using interface Noisable.  Since every class that implements Noisable 
>will be required to have a makeSound() method anyway, what does the empty
>interface bring to the table?

Jumping in here ...

Extra levels of abstraction usually help clarify the design but 
specifically the advantage here is that the caller (i.e., the user of a 
class that implements Noisable) doesn't need to know at compile time the 
particular Noisable type that will be used at run time.  It just creates 
instances of Noisable and invokes the makeSound() method on them.  The 
result is simpler cleaner code.

Similar behaviour may seem possible by creating instances of a Noisable 
class but since the implementation is still likely to be done in 
subclasses you are likely to find that you will at some point need to 
downcast Noisable to the actual class being used.  That's messy and means 
the caller needs to know the actual type.  Interfaces avoid that mess.

The using class will certainly need to load the appropriate instance of a 
Noisable object at run time (a class that implements Noisable) but the 
name of that class can be stored in a properties file and loaded at run 
time via ClassForName.  Because the class is a Noisable object no casting 
is necessary.

You could also do this by creating a class called Noisable instead of an 
interface and that choice is really a design decision.  There are three 
approaches:

        1/ A concrete Noisable class with a default implementation of 
makeSound() which might simply make the speaker buzz.

        2/ An abstract Noisable class with an abstract makeSound() method 
which must be overridden by a subclass.

        3/ A Noisable interface with an implicitly abstract makeSound() 
method which must be implemented by an implementor. 

Which do you choose?  Well, if you can provide a default implementation 
then creating a concrete class makes sense.  The class can be instantiated 
and will work on all systems that have a speaker.  Specific sound hardware 
can supply a subclass of Noisable that overrides makeSound() to fully 
utilise the hardware.

However, not all systems have a speaker (a certain black box springs to 
mind :) so maybe a default implementation of makeSound() doesn't make 
sense.  In that case we could create an abstract class containing an 
abstract (i.e., empty) makeSound() method.  Any subclass must provide an 
implementation of makeSound().  Conceptually this is similar to the 
default concrete class but the class designer doesn't have to provide a 
default implementation.

Now, if the only thing the abstract class contains is an abstract method 
then it probably serves no real purpose other than as a contract requiring 
subclasses to implement their own version of makeSound().  If that is true 
then it would make more sense to define Noisable as an interface.  Using 
an interface also allows the class to inherit from some other class that 
is more representative of its real type.

Note: If there are other methods that can be implemented concretely (e.g., 
setVolume(), setVoice(), etc) then an abstract class with concrete methods 
setVolume() and setVoice, and an abstract makeSound() method would be more 
appropriate.

There are additional questions to be asked during the design to help 
decide whether something should be implemented as a class or an interface.  
Specifically, if the thing you are modeling has an "IS KIND OF" 
relationship then it probably should be a class (concrete if you can model 
all of its behaviour, abstract if you can't).  However, if it simply 
exhibits certain implementation specific behaviour then it probably should 
be an interface.

To use more familiar objects, let's say we are modeling vehicles.  We 
could start with a generic Vehicle class which handles those things common 
to vehicles (number of whee
, number of doors, colour, model, etc).  Then 
we can create a subclass called Car which models passenger vehicles.  We 
can create a subclass called Truck which models load-carrying vehicles.  
Fairly straightforward hierarchy so far.  Now, how do we model a Ute?  (A 
Ute, for the non-Antipodeans amongst us, is comparable to a pickup.)

A Ute exhibits traits common to both Car and Truck.  Hmm ... how can an 
object be two things at once?  In the C++ world that may lead us onto the 
perilous path of multiple inheritance and into Terra Incognita -- you 
know, the bits on olde worlde mappes marked Here be Dragons!

In Smalltalk and Java we don't have the soft option of multiple 
inheritance so a different approach is needed.  We could say a Ute is 
mostly used for carrying loads and therefore is mostly a Truck.  Since it 
does have some Car-like behaviour we can get that behaviour by creating a 
private instance of Car inside the Ute class.  We get Truck-like behaviour 
by direct inheritance and Car-like behaviour by containment.  We would 
never expose the internal Car object iteself to users of Ute but we will 
certainly have to expose some of the Car methods.  If do not need to 
change much of the Car behaviour this is a good approach.  However, if the 
Ute needs to alter much of the Car behaviour then a different approach 
would be sensible.  For instance, we could create a Car interface which 
defines the minimum behaviour needed for an object that is "A KIND OF" 
car.  The Car class implements this interface and provides Car-specific 
implemetations of the abstract methods.  Ute still inherits from Truck but 
also implements the Car interface and provides Ute-specific 
implementations of the Car-like methods.

To return to the Noisable example (but I don't like that name so I'll 
change it) let's say we are creating sound card drivers.  The first 
question is exactly what KIND of thing is a sound driver?  It's really a 
special case of a general device driver.  So we could model the hierarchy 
as:
        Object
                DeviceDriver
                        SoundDriver

Now a SoundDriver is likely to be different for different sound cards.  So 
each sound card would require its own implementation of SoundDriver.  It 
might also be possible to provide a GenericSoundDriver if there are 
similarities between cards.  So we could have:

        Object
                DeviceDriver
                        SoundDriver
                                GenericSoundDriver
                                        CrysalSoundDriver
                                        SoundBlasterSoundDriver
                                EssSoundDriver

but I think it unlikely that vendor specific sound drivers could truly 
inherit from a generic sound driver and I also think that the SoundDriver 
class is likely to be purely abstract thus indicating that its only 
purpose to describe the minimum required methods of a SoundDriver type.  
If you accept the argument that the various sound drivers are just a 
special case of device driver then that suggests SoundDriver ought to be 
an interface and we get:

        Object
                SoundDriver             <=== interface
                DeviceDriver
                        GenericSoundDriver implements SoundDriver
                        CrystalSoundDriver implements SoundDriver
                        SoundBlasterSoundDriver implements SoundDriver
                        EssSoundDriver implements SoundDriver
                        
This hierarchy says that the different sound driver classes are "A KIND 
OF" device driver that happen to implement the SoundDriver behaviour.  
They can be manipulated by a using class AS IF they are device drivers 
(good for loading, initializing, and unloading the driver) and also AS IF 
they are sound drivers (good for playing tunes).

Even though all the sound drivers implement methods with exactly same 
signature the code in each method is likely to be quite different due to 
the different underlying hardware.  The really neat thing is that all the 
sound driver classes can be treated in exactly the same way by the using 
classes without needing to know anything about them other than that they 
are a kind of SoundDriver -- and no casting is needed.  Using an interface 
in this manner can also clarify the intent of the design

The use of interfaces in this fashion also allow even more specialised 
implementations.  Assume one of our sound cards not only plays files from 
disk but can communicate with a CD drive.  We can easily accomodate that 
behaviour by creating a new interface called CdPlayer and implement that 
in addition to the SoundDriver interface:

public class CrystalSoundDriver extends DeviceDriver implements 
SoundDriver, CdPlayer {}

Now our specific implementation is a DeviceDriver type that also exhibits 
the behaviour of a SoundDriver AND the behaviour of a CdPlayer.  It is 
still a device driver and sound driver but now it can also be a CD player.  
Again CD player behaviour is probably similar between different devices 
but the implementation will certainly be different thus suggesting an 
interface rather than an abstract class.

Many Java books treat interfaces as a means of implementing multiple 
inheritance but that is a really bad way to view interfaces and nothing 
other than the contract is really inherited.  Interfaces are way of 
specifying the intent of a design.  Containment is a way of dealing with 
multiple inheritance.

If you've read this far then you've probably got enough to think about and 
I'll stop for the moment ... :)  I hope at least some of this helps.

>If this is way too "newbie" tell me to go away and read some more.  I 
>will do the right thing, I promise!

Yeah, sure ... I've heard that before :)  I don't regard this stuff as 
'too newbie'.  It is fundamental to OO.  It takes some thought and 
practice to begin getting it right -- and frequently a mentor to guide 
you.

Regards,
Simon Coulter.

«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
«» FlyByNight Software         AS/400 Technical Specialists       «»
«» Eclipse the competition - run your business on an IBM AS/400.  «»
«»                                                                «»
«» Phone: +61 3 9419 0175      Mobile: +61 0411 091 400           «»
«» Fax:   +61 3 9419 0175      mailto: shc@flybynight.com.au      «»
«»                                                                «»
«» Windoze should not be open at Warp speed.                      «»
«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
+---
| This is the JAVA/400 Mailing List!
| To submit a new message, send your mail to JAVA400-L@midrange.com.
| To subscribe to this list send email to JAVA400-L-SUB@midrange.com.
| To unsubscribe from this list send email to JAVA400-L-UNSUB@midrange.com.
| Questions should be directed to the list owner: joe@zappie.net
+---

As an Amazon Associate we earn from qualifying purchases.

This thread ...


Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.