/** Abstract classes can be used to ensure that the subclass provides some
    functionality without providing it in the superclass, while ensuring that
    it must be supplied by the subclass.  */

/* Movies, symphonies, operas are all special forms of attraction.  Every
   attraction must provide a rating and a category. */
abstract class 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 ();

  /* Consolidate reporting by referring to the sub-class methods. */
  public String report () {
    return (this.category() + " rating = " +
	    this.rating() + ", duration = " +
	    this.duration() );
  }     

  /* 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 AbstractDemo {

  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 (s.report ());
    System.out.println (c.report ());
    System.out.println (o.report ());

  }

}
