// Purpose.  Observer design pattern

// 1. Model the "independent" functionality with a "subject" abstraction
// 2. Model the "dependent" functionality with "observer" hierarchy
// 3. The Subject is coupled only to the Observer base class
// 4. Observers register themselves with the Subject
// 5. The Subject broadcasts events to all registered Observers
// 6. Observers "pull" the information they need from the Subject
// 7. Client configures the number and type of Observers

class Subject {                                     // 1. The "independent" abs
   private Observer[] observers = new Observer[9];  // 3. Coupled to base class
   private int        totalObs  = 0;
   private int        state;
   public void attach( Observer o ) { observers[totalObs++] = o; } // 3. Coupled
   public int  getState()           { return state; }
   public void setState( int in ) {
      state = in;
      notifyObservers(); }
   private void notifyObservers() {
      for (int i=0; i < totalObs; i++)
         observers[i].update();         // 3. Coupled to base class
}  }                                    // 5. Broadcast events to observers

abstract class Observer {               // 2. Root of the "dependent" hierarchy
   protected Subject subj;
   public abstract void update(); }

class HexObserver extends Observer {    // 2. Concrete class of the "dependent"
   public HexObserver( Subject s ) {    //    hierarchy
      subj = s;  subj.attach( this ); } // 4. Observers register themselves
   public void update() {
      System.out.print( "  " + Integer.toHexString( subj.getState() ) );
}  }                                    // 6. Observers "pull" information

class OctObserver extends Observer {
   public OctObserver( Subject s ) {
      subj = s;  subj.attach( this ); } // 4. Observers register themselves
   public void update() {
      System.out.print( "  " + Integer.toOctalString( subj.getState() ) );
}  }                                    // 6. Observers "pull" information

class BinObserver extends Observer {
   public BinObserver( Subject s ) {
      subj = s;  subj.attach( this ); } // 4. Observers register themselves
   public void update() {
      System.out.print( "  " + Integer.toBinaryString( subj.getState() ) );
}  }                                    // 6. Observers "pull" information

public class ObserverDemo {
   public static void main( String[] args ) {
      Subject sub = new Subject();
      // 7. Client configures the number and type of Observers
      new HexObserver( sub );  new OctObserver( sub );  new BinObserver( sub );
      while (true) {
         System.out.print( "\nEnter a number: " );
         sub.setState( Read.anInt() );
}  }  }

// Enter a number: 15
//   f  17  1111
// Enter a number: 17
//   11  21  10001
// Enter a number: 31
//   1f  37  11111



// Purpose.  Observer design pattern, class inheritance vs type inheritance

// SensorSystem is the "subject".  Lighting, Gates, and Surveillance are the
// "views".  The subject is only coupled to the "abstraction" of AlarmListener.

// An object's class defines how the object is implemented.  In contrast, an
// object's type only refers to its interface.  Class inheritance defines an
// object's implementation in terms of another object's implementation.  Type
// inheritance describes when an object can be used in place of another.
// [GOF, pp13-17]

interface AlarmListener { public void alarm(); }

class SensorSystem {
   private java.util.Vector listeners = new java.util.Vector();

   public void register( AlarmListener al ) { listeners.addElement( al ); }
   public void soundTheAlarm() {
      for (java.util.Enumeration e=listeners.elements(); e.hasMoreElements(); )
         ((AlarmListener)e.nextElement()).alarm();
}  }

class Lighting implements AlarmListener {
   public void alarm() { System.out.println( "lights up" ); }
}

class Gates implements AlarmListener {
   public void alarm() { System.out.println( "gates close" ); }
}

class CheckList {
   public void byTheNumbers() {  // Template Method design pattern
      localize();
      isolate();
      identify(); }
   protected void localize() {
      System.out.println( "   establish a perimeter" ); }
   protected void isolate()  {
      System.out.println( "   isolate the grid" ); }
   protected void identify() {
      System.out.println( "   identify the source" ); }
}
                   // class inheri.  // type inheritance
class Surveillance extends CheckList implements AlarmListener {
   public void alarm() {
      System.out.println( "Surveillance - by the numbers:" );
      byTheNumbers(); }
   protected void isolate() { System.out.println( "   train the cameras" ); }
}

public class ClassVersusInterface {
   public static void main( String[] args ) {
      SensorSystem ss = new SensorSystem();
      ss.register( new Gates()        );
      ss.register( new Lighting()     );
      ss.register( new Surveillance() );
      ss.soundTheAlarm();
}  }

// gates close
// lights up
// Surveillance - by the numbers:
//    establish a perimeter
//    train the cameras
//    identify the source