Hint til oppgave 8

Dette er en omfattende oppgave, og det lønner seg å bygge den opp i flere steg.

Man må lage klasser for å representere datastrukturen (objektsamlinger), og også klasser for brukergrensesnittet.

Brukergrensesnittet

Brukeren skal kunne registrere data om både varer, kunder og bestillinger. Det kan f.eks. gjøres i tre vinduer, slik:

Tanken er at programmet leser inn data fra fil(er) ved oppstart, og viser opplysninger om ett objekt av gangen (én vare, én kunde, én bestilling). Brukeren kan navigere i objektsamlingene med knapper Forrige og Neste, samt legge til (Ny) og slette (Slett).

Ved redigering av tekstfeltene kunne man valgt å lytte til samtlige tekstfelt, og oppdatert objektsamlingene "automatisk". Her er det lagt opp til en litt enklere variant, med en egen knapp Oppdater. NB! I løsningsforslaget vil Oppdater ha effekt på objektsamlingene i hurtigminnet, men ikke skrive opplysningene til fil.

Plassering av tekstfelt med tilhørende etiketter kan f.eks. gjøres med GridLayout. Hvis hele vinduet er en JFrame med BorderLayout, kan panelet som skal inneholde etiketter og tekstfelt opprettes slik for kundeskjemaet:

JPanel pnlSkjema = new JPanel();

// 3 rader og 2 kolonner med 5 piksler luft horisontalt og vertikalt
GridLayout gridLayout = new GridLayout(3, 2, 5, 5);
pnlSkjema.setLayout(gridLayout);

add(pnlSkjema, BorderLayout.CENTER);
// Legg deretter knappene i et eget panel, som plasseres i sør.

Merk at varenr, kundenr og bestillingsnr er "grået ut". I løsningsforslaget sørger programmet for å "autogenerere" disse løpenummerne, og brukeren får dermed ikke lov til å skrive inn noe i disse tekstfeltene.

Skjemaet for Bestillinger byr på en ekstra utfordring. I en fullgod løsning bør man tillate vilkårlig mange varer på en og samme bestilling. Løsningsforslaget forenkler litt, og begrenser dette til 5 varer pr. bestilling. Dermed kan man bygge opp vinduet på samme måte for alle bestillinger.

De tre skjemaene kan programmeres som subklasser av JFrame på vanlig måte, men bør ikke avslutte programkjøring når brukeren lukker vinduet. I stedet kan man lage en egen "hovedmeny" som vises ved oppstart:

Under menyen Fil kan man legge et valg for å skrive (oppdaterte) objektsamlinger til fil. Husk at lagring også skal gjøres når brukeren avslutter programmet ved å lukke hovedvinduet.

Knappene Varer, Kunder og Bestillinger kan sørge for å gjøre de ulike vinduene synlige:

  vareGui.setVisible(true);

Datamodellen

Hvis man skulle lagret data om varer, kunder og bestillinger i en database, ville følgende tabellstruktur vært en mulig løsning:

Varer, kunder og bestillinger er identifisert av et unikt løpenr, som er gitt navnet Nr i alle tre tabeller. Hver bestilling er knyttet til en kunde (KundeNr), og kan ha et antall bestillingslinjer, som hver inneholder et varenr og et antall.

Kommentar: I databaseterminologi vil Nr være primærnøkkel i Vare, Kunde og Bestilling, BestNr+VareNr vil være primærnøkkel i Bestillingslinje. Et databasesystem vil sjekke at det f.eks. ikke blir registrert to varer med samme nummer. Dessuten vil KundeNr i Bestilling være fremmednøkkel mot Kunde, og VareNr vil være fremmednøkkel mot Vare. Databasesystemet vil sjekke at kunder og varer som blir referert fra en bestilling faktisk eksisterer i tabellene Vare og Kunde.

Lagring på fil

En grei løsning er å lagre data på kommaseparerte tekstfiler. Man kan velge å bruke fire filer med samme oppbygging som databasetabellene skissert over, men i løsningsforslaget er det valgt å lagre Bestilling og BestillingsLinje i én fil.

vare.dat:

1;Spade;220.50;
2;Hakke;199.00;
3;Spett;170.00;

kunde.dat:

1;Per Hansen;Kongens gate 3;
2;Lise Mo;Bjørkelia 17;

bestilling.dat:

1;20.03.2013;1;2;6;
2;20.03.2013;2;1;1;3;2;

Andre linje sier at bestilling nr 2 er registrert 20.03.2013 og tilhører kunde nr. 2. Til denne bestillingen hører det to bestillingslinjer, 1 enhet av vare nr. 1, og 2 enheter av vare nr. 3.

Objektsamlinger

Start med håndtering av varer (eller kunder). Lag to klasser:

Klassen Vare kan bygges opp på vanlig måte, med konstruktører og set/get-metoder. Lag gjerne en konstruktør som bygger opp Vare-objektet fra en String, der tekststrengen er slik varen blir lagret på fil, og en tilsvarende metode som gjør om et Vare-objekt til en tekststreng for lagring til fil.

Klassen VareSamling skal være "modellen" bak skjemaet for registrering av varedata. Den må opplagt kunne ta vare på et antall varer. Man kan f.eks. bruke ArrayList til dette.

Fordi skjemaet kun viser én vare av gangen, er det behagelig om VareSamling også tar vare på hva som nå er "aktiv" vare. Det kan gjøres med en heltallsvariabel pos.

For håndtering av autonummerering kan man innføre nok en heltallsvariabel nesteNr.

Forslag til objektvariabler og metoder i VareSamling:

public class VareSamling {

  private ArrayList<Vare> liste;    // Varesamlingen
  private int             pos;      // Aktiv vare
  private int             nesteNr;  // Neste løpenr

  // Sett inn en ny vare med et friskt løpenr
  public void ny()             { ... }
  
  // Slett vare med gitt nr
  public boolean slett(int nr) { ... }

  // Returner aktiv vare
  public Vare denne()          { ... }
  
  // Gå til forrige vare
  public void forrige()        { ... }
  
  // Gå til neste vare
  public void neste()          { ... }
  
  // Les inn varesamlingen fra fil
  public void lesFraFil()      { ... }

  // Skriv (oppdatert) varesamling til fil  
  public void skrivTilFil()    { ... }
}

Lag deretter en tilsvarende løsning for kunder, og til slutt bestillinger. Denne siste er mer krevende, fordi den må inneholde en liste med "bestillingslinjer". Lag en hjelpeklasse BestillingsLinje med objektvariabler for å ta vare på varen og antallet. Klassen Bestilling kan deretter gis en objektvariabel liste (i tillegg til løpenr, dato og kunde):

  private ArrayList<BestillingsLinje> liste;

Bruk av subklasser

De tre objektsamlingene har noen fellestrekk:

Dette antyder en løsning med subklasser.

I løsningsforslaget er det valgt å opprette en superklasse BrimiObjekt over Vare, Kunde og Bestilling. Her blir selve løpenummeret lagret. Dessuten er det opprettet en objektsamling BrimiSamling, som kan inneholde enten Vare-objekt, Kunde-objekt eller Bestilling-objekt.

Prøv gjerne å bygge om en første løsning på denne måten, der du altså vil oppnå å erstatte de tre klassene VareSamling, KundeSamling og BestillingSamling med en mer generell løsning BrimiSamling.

Også de tre vinduene har en god del fellestrekk, som fører til gjentak av nesten identisk kode. Prøv gjerne å lage en felles superklasse for vinduene. Den kan bla. ta seg av knapperaden.

Mangler i løsningsforslaget

Det er sikkert flere ting, men her er i hvert fall to opplagte forbedringer: