6.6. Matadorspillet version 2

Dette eksempel viser, hvordan man kan spare programkode (og dermed programmeringstid) med nedarvning. Samtidig viser det brugen af konstruktører i underklasser.

Se igen på programkoden til Rederi og Gade. Der er meget programkode, som er ens for de to klasser. Faktisk implementerer de kode, der er fælles for alle grunde, der kan ejes af en spiller, og derfor vil det være hensigtsmæssigt, at følgende kode var i en Grund-klasse:

Det har vi gjort herunder. Vi har været forudseende og flyttet beregningen af lejen ud fra landet() og ind i en separat metode beregnLeje(), fordi netop denne er meget forskellig for Rederi og Gade.


// Mellemklasse mellem 'Felt' og underliggende klasser som Gade og Rederi

public class Grund2 extends Felt
{
  Spiller ejer;
  double pris;
  double grundleje;

  public Grund2(String navn, double pris, double leje)
  {
    this.navn=navn;
    this.pris=pris;
    this.grundleje=leje;
  }

  public double beregnLeje()
  {
    return grundleje;
  }

  public void landet(Spiller sp)
  {
    System.out.println(sp.navn+" er landet på "+navn);
    if (sp==ejer)
    {                                       // spiller ejer feltet
      System.out.println("Dette er "+sp.navn+"s egen grund");
    }
    else if (ejer==null)
    {                                       // ingen ejer grunden, så køb den
      if (sp.konto > pris)
      {
        System.out.println(sp.navn+" køber "+navn+" for "+pris);
        ejer=sp;
        sp.transaktion( -pris );
      }
      else System.out.println(sp.navn+" har ikke penge nok til at købe "+navn);
    }
    else
    {                                       // felt ejes af anden spiller
      double leje = beregnLeje();
      System.out.println("Husleje: "+leje);
      sp.betal(ejer, leje);                 // spiller betaler til ejeren
    }
  }
}

Nu er Rederi ret nem. Den skal nemlig (i denne simple udgave) opføre sig præcis som Grund. Vi skal blot definere konstruktøren, som skal kalde den tilsvarende konstruktør i Grund:


// Rederier

public class Rederi2 extends Grund2 
{
  public Rederi2(String navn, double pris, double leje)
  {
    super(navn, pris, leje);    // kald superklassens konstruktør
  }
}

Nu kommer vi til Gade. Her er beregnLeje() tilsidesat til også at tage højde for antallet af huse. Med super kan vi faktisk spare en hel del arbejde. Gaderne kan genbruge meget af landet()-metoden, men der er dog en ekstra mulighed for at bygge hus. Derfor kalder vi superklassens landet()-metode, hvis spilleren, der er landet på gaden, ikke er ejeren. Hvis det er ejeren, prøver vi at bygge et hus (udskilt i metoden bygHus()).


// En gade der kan bebygges

public class Gade2 extends Grund2
{
  int antalHuse;                                      // antal huse og pris
  double huspris;

  public Gade2(String navn, double pris, double leje, double huspris)
  {
    super(navn, pris, leje);
    this.huspris=huspris;
    antalHuse = 0;
  }

  public double beregnLeje()                          // tilsidesæt Grund2's
  {
    return grundleje + antalHuse * huspris;
  }

  public void landet(Spiller sp)
  {
    if (sp==ejer)
    {                                                 // eget felt; byg hus
      System.out.println(sp.navn+" er landet på "+navn);
      System.out.println("Dette er "+sp.navn+"s egen grund");
      if (antalHuse<5 && sp.konto>huspris) bygHus();  // byg hus hvis vi kan
    }
    else super.landet(sp);                            // brug gamle landet()
  }

  public void bygHus()
  {
    System.out.println(ejer.navn+" bygger et hus på "+navn+" for "+huspris);
    ejer.transaktion( -huspris );
    antalHuse = antalHuse + 1;
  }
}

Læg mærke til, at vi har sparet næsten halvdelen af koden væk i de to nye klasser.

Herunder ses klassediagrammet for de nye klasser. Da Grund2 har en spiller (ejeren), er der en pil fra Grund2 til Spiller, en har-en-relation. Resten af pilene symboliserer er-en-relationer, f.eks. Gade2 er en Grund2, Grund2 er et Felt.

Figur 6-9. Java