Index: [thread] [date] [subject] [author]
  From: Garrett Mitchener <wgm2@acpub.duke.edu>
  To  : 
  Date: 28 Apr 1999 11:13:47 -0400

Re: instanceof

Jay Steele <jes22@duke.edu> writes:

The problem that we seem to be getting into is how to deal with that
extra argument that observables pass to their observers when they say
"I've changed, update yourselves."

You are right that the observables should not have too much knowledge
of what their observers are doing.  You should separate the item
itself from the visual representation of that item.

However, when you write an observer class, it shouldn't be a god class
that can observe and draw anything.  I don't know if anyone is doing
this, but I'm in the mood to create some chaos, so consider this:

public class Foo extends Observable { ... }
public class Bar extends Observable { ... }

public class EvilGodObserver implements observer
{

  public void update( Observable obj, Object arg )
  {
    if(obj instanceof Foo )
    {
      drawFoo( (Foo)obj );
    }
    if( obj instanceof Bar )
    {
      drawBar( (Bar)obj );
    }
    ...
  }
}

(Notice where I've put the instanceof in this case-- it's on obj not
on arg like it was in a previous post.  I'll handle that in a minute.)

Instead of that, you should have different kinds of observers, and you
never ask a FooRenderer to observe a Bar:

public class FooRenderer implements Observer
{
  public void update( Observable obj, Object arg )
  {
     Foo foo = (Foo)obj;
     // proceed to draw a Foo.
  }
}

public class BarRenderer implements Observer { ... } 

Foo aFoo = new Foo();
Bar aBar = new Bar();

aFoo.addObserver( new FooRenderer() );
aBar.addObserver( new BarRenderer() );

(That cast to Foo should always succeed.  If you accidentally add a
FooRenderer to a Bar as an observer, then the cast will fail and your
program will crash with a class cast exception.  This crash should be
treated like a seg fault.  It indicates that your program is buggy,
and there is no way for your program to properly handle such an
exception other than to crash.)

This is how you separate the view of the data from the data itself.
It doesn't use any sort of run-time type checking, and it has the
advantage that if you now write class Baz (extends Observable) and a
BazRenderer, you don't have to change any of the code for Foo, Bar,
FooRenderer, or BarRenderer.  If you used the EvilGodObserver instead,
you would have to edit that class and add a new case for drawing
Bazzes, which is an OO no-no.  You might not have the source code.

ONE POINT OF OOP IS TO GET RID OF THAT KIND OF NON-EXTENSIBILITY.

I think the kink in this discussion is that the Java language and
design of java.util classes forces us to deal with these variables
that are of type Object but if you design and implement your code
properly you know exactly what kind of object you're observering and
there's no point in doing type checking.  Just cast it, and if it
fails, you've found a bug.

Dealing with the other parameter, arg: If you use the above
multiple-observer design, you should have a convention of what kind of
object arg has to be, even though the variable is of type Object.  It
should be some kind of hint as to what has been changed in the
Observable obj.  (You don't even have to use that parameter -- I've
never used it in fact.  I just ignore it and assume the whole object
has changed.  If I need any more information than that, I write my own
observer stuff.)  What a Foo passes to its FooObservers may very well
be different from what Bar passes to its BarObservers, but if you
never ask a FooObserver to observe a Bar, it's not a problem: You just
cast arg to a FooObserverHint or a BarObserverHint, respectively, and
use that.  If the cast fails, it means your program is buggy.

You might even be better off writing your own observer/observable
classes which have the proper types:

public interface FooObserver
{
  public void updateFoo( Foo obj, int lineNumberThatChanged );
}

public class Foo // no longer extends observable
{
  public void addFooObserver( FooObserver o )
  {
  ...
  }
}

As for using the command interface on arg, that was a bad choice of
examples, and I apologize.  You might prefer something like this
instead:

public interface EditorHints
{
  public int getLineThatChanged();
  public boolean fileSaved();
  public boolean fileClosed();
  etc.
}

public class EditorObserver implements Observer
{
  public void update(  Observable obj, Object arg )
  {
    Editor e = (Editor)obj;
    EditorHints h = (EditorHints)arg;
    ...
  }
}

However, if there gets to be too much stuff in your hints (as it looks
like will soon be the case in this example), you should break it down
further, so that you can add different kinds of Observers watching
different parts of the same object (like listeners in AWT):

public class Room
{
   public void addCharacterEntersListener( CharacterEntersListener l )
   {...}

   public void addItemAppearsListener( ... )
   public void addMagicHappensListener(...)

}

This relatively simple question of instanceof and observers seems to
have drummed up some design questions.  Good! Keep them coming!
Remember that as TA's we can give advice and hints, but you should
also be learning for yourself what makes a design good or bad, not
just blindly or almost-blindly following something someone may have
said.  You need to understand why they said it and under what
conditions it applies.

	-- Prince Vlad

> I thought that one of the reasons for implementing observers was to
> separate the output from the other aspects of the game.  To me, this seems
> to imply that objects such as players and items should have no idea how to
> output themselves.  All output would  be handled by the observer.  So, how
> would you determine what the object is (the user, an enemy, a network
> player) with out using instanceof?  If you use the command interface as
> suggested, to me it means that somewhere outside of the observer it is
> being decided how stuff, such as players, should be outputted.  This seems
> to create a conflict in what we have been told to do.  Is it now okay to
> determine output before reaching the observers?
> 
> jay
> 


Index: [thread] [date] [subject] [author]