Automat.java [Last ned]

/* 
 *  Modellklasse for å simulere en enkel automat.
 *  Denne versjonen kan levere kafffe, sjokolade og brus.
 *
 *  En bestilling gjennomføres ved først å legge på
 *  mynter (metoden leggPå kalles et antall ganger),
 *  og så velge produkt (metoden velgProdukt kalles
 *  1 gang). I det produkt velges vil automaten sjekke
 *  om bestillingen lar seg gjennomføre, beregne
 *  vekslepenger, telle ned beholdningen av penger
 *  produkt (hvis mulig å levere). Status om bestillingen
 *  kan deretter avleses med metodene valgtProdukt,
 *  sumLagtPå og statusmelding. For testing finnes også
 *  en metode toString som viser innholdet i automaten.
 *
 *  Mulig generalisering:
 *  Data om konkrete produkt og myntenheter lagres i
 *  et antall tabeller. Ved å sende med innholdet i 
 *  disse tabellene som parametre til konstruktøren 
 *  (eller legge på metoder for å "fylle på" automaten)
 *  kan klassen Automat gjøres helt uavhengig av
 *  konkrete produkt og myntenheter. Slik det er nå
 *  må klassen redigeres og rekompileres ved endring
 *  (nye produkt og/eller myntenheter).
 *
 *  En mer objektorientert variant:
 *  Ved å innføre hjelpeklasser Produkt og Mynt kan
 *  beholdningen i automaten representeres ved to
 *  referansetabeller (erstatter de fem tabellene
 *  prodNavn, prodPriser, produkt, myntEnheter og mynter).
 *
 */
 
public class Automat {
 
  // Konstanter som oppsummerer status for siste bestilling
  private static final int IKKE_AVSLUTTET   = 0;
  private static final int OK               = 1;
  private static final int UTSOLGT          = 2;
  private static final int LAGT_PÅ_FOR_LITE = 3;
  private static final int KAN_IKKE_VEKSLE  = 4;
 
 
  // Standardbeholdning av produkt ved oppstart
  private String[] prodNavn    = { "Kaffe", "Sjokolade", "Brus" };
  private int[]    prodPriser  = { 5,       8,           12     };
  private int[]    produkt     = { 50,      50,          50     };
 
  // Standardbeholdning av mynter ved oppstart
  // For at beregning av vekslepenger skal bli korrekt
  // må myntenhetene være sortert stigende.
  private int[]    myntEnheter = { 1,  5,  10, 20 };
  private int[]    mynter      = { 50, 50, 50, 50 };
 
 
  // Aktiv bestilling
  private int      lagtPå;        // Sum lagt på
  private String   valgtProdukt;  // Hvilket produkt er valgt (hvis noen)
  private int      status;        // Status for bestillingen
  private int      retur;         // Beløp som skal tilbakebetales
  private int[]    vekslepenger;  // Vekslepenger
 
 
 
 
  // Initiering.
  public Automat() {
    vekslepenger = new int[myntEnheter.length];
    nullstill();
  }
 
 
  // Legg en ny mynt på automaten.
  // Returnerer true hvis mynten er lovlig.
  public boolean leggPå( int mynt ) {
 
    // Er dette første mynt i en ny bestilling?
    if (status != IKKE_AVSLUTTET)
      nullstill();
 
    // Sjekk at mynten er lovlig
    int pos = myntPos(mynt);
 
    if (pos>=0) {
 
      // Oppdater sum lagt på i aktiv bestilling
      lagtPå = lagtPå + mynt;
 
      // Oppdater mynt-samlingene
      mynter[pos]++;
    }
 
    return (pos>=0);
  }
 
 
  // Velg produkt.
  // Returnerer true hvis produktet er lovlig.
  // Hvis lovlig produkt beregnes også vekslepenger,
  // og både produktbeholdning og myntbeholdning blir
  // talt ned.
  public boolean velgProdukt( String produkt ) {
    valgtProdukt = produkt;
    int pos = prodPos(produkt);
 
    if (pos>=0) {             // Lovlig produkt
      sjekkProdukt(pos);      // Utsolgt?
      sjekkBeløp();           // Lagt på nok penger?
 
      if (mulig())
        if (!beregnVekslepenger(retur))  // Prøv å veksle
          status = KAN_IKKE_VEKSLE;
 
      if (!mulig())
        beregnVekslepenger(lagtPå); // Gi tilbake det som er lagt på
 
      leverVekslepenger();
      if (mulig())
        leverProdukt(pos);
    }
 
    // Hvis brukeren velger et lovlig produkt blir bestillingen "avsluttet",
    // men ikke nødvendigvis med suksess. Hvis det ikke er mulig blir bare
    // pengene som er lagt på betalt ut, og statusmeldingen viser feilen
    // (f.eks. at automaten er utsolgt).
    if (pos>=0 && mulig())
      status = OK;
 
    return pos>=0;
  }
 
 
  // Hvilket produkt er valgt?
  // Returnerer null hvis produkt ikke er valgt.
  public String valgtProdukt() {
    return valgtProdukt;
  }
 
 
  // Hvor mye penger er lagt på i aktiv bestilling?
  public int sumLagtPå() {
    return lagtPå;
  }
 
 
  // Returnerer vekslepengene for aktiv bestilling,
  // og -1 hvis mynt er ulovlig/ukjent.
  public int vekslepenger(int mynt) {
    int pos = myntPos(mynt);
 
    if (pos>=0)
      return vekslepenger[pos];
    else
      return -1;
  }
 
 
  // Statusmelding for siste bestilling
  public String statusmelding() {
    String melding = null;
 
    if (status == IKKE_AVSLUTTET)
      melding = "Produkt er ikke valgt!";
    else if (status == UTSOLGT)
      melding = "Tom for " + valgtProdukt + "!";
    else if (status == LAGT_PÅ_FOR_LITE)
      melding = "Legg på mer penger!";
    else if (status == KAN_IKKE_VEKSLE)
      melding = "Kan ikke veksle!";
    else if (status == OK)
      melding = "Værsågod - her er " + valgtProdukt
                +  " og " + retur + " kr. tilbake.";
 
    return melding;
  }
 
 
  // Presenterer tilstanden til automaten.
  // Nyttig under testing.
  public String toString() {
    String retur = visBestilling()
                 + visProdukt()
                 + visMynter()
                 + "\n";
    return retur;
  }
 
 
  // Fyller på produkt, brukes ved testing.
  // Parameter antall er totalt antall etter påfyll.
  public void settProduktBeholdning(String p, int antall) {
    int pos = prodPos(p);
    produkt[pos] = antall;
  }
 
 
  // Fyller på produkt, brukes ved testing.
  // Parameter antall er totalt antall etter påfyll.
  public void settMyntBeholdning(int myntEnhet, int antall) {
    int pos = myntPos(myntEnhet);
    mynter[pos] = antall;
  }
 
 
  // H J E L P E M E T O D E R
 
 
  // Sjekker at det er lagt på nok penger.
  private void sjekkBeløp() {
    int pos = prodPos(valgtProdukt);
    if (pos>=0)
      if (lagtPå < prodPriser[pos])
        status = LAGT_PÅ_FOR_LITE;
      else
        retur = lagtPå - prodPriser[pos];
  }
 
 
  // Sjekk at produktet finnes og at det er minst 1 igjen.
  private void sjekkProdukt(int pos) {
    if (produkt[pos] == 0)
      status = UTSOLGT;
  }
 
 
  // Beregner hvor mange av hver mynt. Forsøker å gi tilbake
  // så store mynter som mulig. Må samtidig sjekke mynt-beholdningen.
  private boolean beregnVekslepenger(int beløp) {
 
    // Starter bakerst med største myntenhet
    int pos = myntEnheter.length-1;
    while (pos>=0 && beløp>0) {
      // Antall mynter av denne enheten, forutsatt stor nok myntbeholdning
      int ønsketRetur = beløp/myntEnheter[pos];
 
      // Tilpasser til myntbeholdningen
      int muligRetur = Math.min(ønsketRetur, mynter[pos]);
 
      // Oppdaterer vekslepenger med antall mynter av denne enheten
      vekslepenger[pos] = muligRetur;
 
      // Teller ned resterende beløp som skal leveres
      beløp -= muligRetur*myntEnheter[pos];
 
      // Ser på neste myntenhet (en mindre mynt)
      pos--;
    }
 
    return (beløp == 0);
  }
 
 
  // Sjekker om bestillingen lar seg gjennomføre.
  // Brukes etter at alle sjekkene er utført.
  private boolean mulig() {
    return status != UTSOLGT 
        && status != LAGT_PÅ_FOR_LITE
        && status != KAN_IKKE_VEKSLE;
  }
 
 
 
  // Finn posisjonen til et produkt (i produkttabellene).
  // Returnerer -1 hvis ikke finnes.
  private int prodPos(String produkt) {
    boolean funnet = false;
    int pos = 0;
    while (!funnet && pos<prodNavn.length) {
      if (produkt.equals(prodNavn[pos]))
        funnet = true;
      else
        pos++;
    }
 
    if (funnet)
      return pos;
    else
      return -1;
  }
 
 
  // Finn posisjonen til en mynt (i mynttabellene).
  // Returnerer -1 hvis ikke finnes.
  private int myntPos(int mynt) {
    boolean funnet = false;
    int pos = 0;
    while (!funnet && pos<myntEnheter.length) {
      if (mynt == myntEnheter[pos])
        funnet = true;
      else
        pos++;
    }
 
    if (funnet)
      return pos;
    else
      return -1;
  }
 
 
  // Leverer vekslepenger, dvs. at myntbeholdningen
  // blir talt ned med antallet i tabellen vekslepenger
  // (som allerede er beregnet).
  private void leverVekslepenger() {
    for (int i=0; i<mynter.length; i++)
      mynter[i] -= vekslepenger[i];
  }
 
 
  // Lever produkt, dvs. at produktbeholdningen
  // blir talt ned med 1 - hvis det er mulig.
  private void leverProdukt(int pos) {
    produkt[pos]--;  
  }
 
 
  // Klargjør for ny bestilling
  private void nullstill() {
    lagtPå = 0;
    valgtProdukt = null;
    status = IKKE_AVSLUTTET;
    retur = 0;
 
    for (int i=0; i<vekslepenger.length; i++)
      vekslepenger[i] = 0;
  }
 
 
 
  // Vis aktiv bestilling
  private String visBestilling() {
    String retur = "Automatens tilstand:\n"
     + "Lagt på: " + lagtPå + "\n"
     + "Valgt produkt: " + valgtProdukt + "\n";
    return retur;
  }
 
 
  // Vis beholdningen av produkt, brukes kun til testing.
  private String visProdukt() {
    String retur = "";
    for (int i=0; i<prodNavn.length; i++) {
      retur += prodNavn[i] + ": " + produkt[i] + "\n";
    }
    return retur;
  }
 
 
  // Vis myntbeholdningen, brukes kun til testing.
  private String visMynter() {
    String retur = "";
    for (int i=0; i<myntEnheter.length; i++) {
      retur += myntEnheter[i] + ": " + mynter[i] + "\n";
    }
    return retur;
  }
 
}
 
 
Kildekode blir vist ved hjelp av GeSHi.