/** Interfaces may be used to specify the public methods of an object.  This
    allows us to write code for any class implementing that interface, so that
    it need not be tied to a particular class.

    Interfaces are a special form of class in which all methods are abstract
    and all fields are static.
*/

interface ATTRACTION {
    public int duration ();
    public int rating ();
    public String category();
}

/* Movies, symphonies, operas are all special forms of attraction.  Every
   attraction must provide a rating and a category. */
abstract class Attraction implements ATTRACTION {

  /* Per-class default duration. */
  private static int defdur = 90;

  /* Per-instance actual duration. */
  private int duration = defdur;

  /* Methods to control access to instance variables. */

  public int duration () {
    return this.duration;
  }

  /* Sub-class methods. */
  abstract public int rating ();
  abstract public String category ();

  /* Constructor. */

  public Attraction (int d) {
    duration = d;
  }

}

/* A movie is a form of attraction. */
class Movie extends Attraction {

  /* Instance variables. */
  private int script, acting, directing;

  /* Constructor. */
  public Movie (int dur, int s, int a, int d) {
    super(dur);			// invoke the superclass constructor
    this.script = s; this.acting = a; this.directing = d;
  }
  
  /* Calculate the rating. */
  public int rating () {
    return (this.script + this.acting + this.directing);
  }

  /* Return a description of the attraction. */
  public String category () {
    return "Movie";
  }

}

class Symphony extends Attraction {

  /* Rating parameters. */
  private int music, playing, conducting;

  /* Constructor. */
  public Symphony (int dur, int m, int p, int c) {
    super(dur);
    this.music = m; this.playing = p; this.conducting = c;
  }

  public int rating () {
    return music + playing + conducting;
  }

  public String category () {
    return "Symphony";
  }

}

/* An opera is a symphony (sort of) with acting and singing.  We override the
   rating method of the superclass to change its behavior for the subclass. */
class Opera extends Symphony {

  /* Rating parameters. */
  private int libretto, singing, acting;

  /* Constructor. */
  public Opera (int dur, int m, int p, int c, int l, int s, int a) {
    super (dur, m, p, c);	// invoke superclass constructor
    this.libretto = l; this.singing = s; this.acting = a;
  }

  /* Calculate rating as a function of the superclass's rating. */
  public int rating () {
    return (super.rating() + this.libretto + this.singing + this.acting);
  }

  public String category () {
    return "Opera";
  }

}

class InterfaceDemo {

  private static String report (ATTRACTION a) {
    return (a.category() + " rating = " +
	    a.rating() + ", duration = " +
	    a.duration());
  }

  public static void main (String argv[]) {

    Symphony s = new Symphony(95, 8, 9, 8);
    Movie c = new Movie (90, 6, 7, 4);
    Opera o = new Opera (180, 8, 9, 8, 7, 8, 8);

    System.out.println (report (s));
    System.out.println (report (c));
    System.out.println (report (o));

  }

}
