Oblig 3 (INF1000 - V?r 2010)

Gulbrand Gr?s Husleiesystem
M?l: Form?let med denne oppgaven er ? gi trening i ? l?se et st?rre programmeringsproblem ved hjelp av klasser og objekter, kombinert med de andre elementene vi har sett p? til n?, som filbehandling, arrayer, metoder, og brukerinteraksjon via terminal.

Leveringsfrist

Fredag 26. mars kl. 16.00.  Leveres via Joly-systemet.  (Joly kan evt. brukes hjemmefra vha. VPN eller lignende.)

Leveringskrav

Besvarelsen din m? bruke minst de 4 klassene beskrevet nedenfor (Oblig3, Utsyn, Hybel, og Student) for ? kunne bli godkjent.  Du skal levere enten én, eller to filer:

Oppgaven skal l?ses individuelt.  Hver student plikter ? ha lest og forst?tt kravene til innleverte oppgaver ved Ifi: http://www.ifi.uio.no/studier/studentinfo.html#krav.

Oppgave

Gulbrand Gr? har et lite hybelhus med 18 studenthybler som han leier ut til studenter i Utopias hovedstad Uqbar.  Du skal hjelpe ham med ? lage et system for ? administrere utleie av hyblene i dette hybelhuset, som heter Utsyn.  Utsyn har 3 etasjer, nummerert fra 1 til 3.  I hver etasje er det 6 hybler, kalt rom A til F, og et fellesrom.  Hver hybel har et entydig ?hybelnavn? som best?r av etasjenummer og rom-bokstav, f.eks. heter hybelen i rom C i andre etasje "2C".

Nedenfor ser du en illustrasjon av en etasje.  Alle etasjene ser like ut.

[Bilde av en etasje i Hybelhus]

?konomi

Guldbrand Gr? leier ut hyblene i de to f?rste etasjene for 5000 kroner i m?neden, mens husleien for en hybel i toppetasjen er 6000 kroner, p? grunn av utsikten.

Guldbrand har beregnet at hans egne utgifter som eier av hyblene er 1200 kroner per hybel per m?ned. Dette inkluderer str?m, Internet, reparasjoner, faste avgifter, avskrivinger, osv., og gjelder for alle hyblene (A?F) uansett om de har beboer eller ikke. Utgiftene for fellesarealene er litt h?yere, siden det er der matlagingen foreg?r, og Gulbrand har beregnet utgiftene hans for fellesarealene til ? v?re 1700 kr. per m?ned per etasje.  Beboerne betaler kun husleiene sine for hyblene A?F, og Gulbrand tar seg av alle andre utgiftene.

Programmet

Programmet skal v?re menystyrt.  Det skal skrive ut p? skjermen en meny over mulige ordre og be brukeren om ? taste en av disse ordrene.  Hvis brukeren taster inn et ulovlig menyvalg, skal det gis feilmelding. Programmet skal g? i l?kke og fortsette ? lese og utf?re ordre helt til brukeren taster ordre '7' for ? avslutte.  Menyvalgene systemet skal ha er:
  1. Skriv oversikt
  2. Registrer ny leietager
  3. Registrer betaling fra leietager
  4. Registrer frivillig utflytting
  5. M?nedskj?ring av husleie
  6. Kast ut leietagere
  7. Avslutt

Datafilen "hybeldata.txt"

N?r programmet starter (og f?r hovedmenyen skrives ut p? skjermen), skal programmet lese datafilen hybeldata.txt.  I denne filen er det lagret blant annet informasjon om hvor lenge systemet har v?rt i drift og navn p? de n?v?rende beoboerne.

F?rste linje i filen inneholder fire verdier adskilt med semikolon, med f?lgende format:
int m?ned; int ?r; int totalfortjeneste; int totaltAntallM?neder
Her er "int m?ned" og "int ?r" n?v?rende m?neds- og ?rstall (eller mer presist, m?nedsnummeret og ?rstallet da m?nedskj?ring sist ble utf?rt), hvor m?ned skal v?re et tall i omr?det 1 til 12, og ?rstallet er f.eks. 2010.  Det tredje tallet er Gulbrands totale fortjeneste siden systemet ble satt i drift; og det siste tallet angir antall m?neder systemet har v?rt i drift.

Deretter er det 18 linjer (en for hver hybel), med f?lgende format:

int etasje; char bokstav; int saldo; String studentnavn

Eksempel p? fire mulige linjer som kan v?re i begynnelsen av datafilen:
3;2010;1400500;24
1;A;6000;Ole Einar
1;B;12000;Tora Berg
1;C;0;TOM HYBEL
For tomme hybler skal studentnavnet lagres i datafilen som TOM HYBEL, med saldo 0.  Du kan anta at alle studenter har unike navn. (Se hint 2 under for tips om lesing av datafilen.)

Alle dise dataene skal holdes oppdatert internt i programmet mens det kj?rer, og skal skrives tilbake til datafilen n?r brukeren utf?rer ordre '7' i hovedmenyen ("Avslutt").  Slik kan Gulbrand starte og avslutte programmet uten ? miste data.

Menyvalgene

  1. Skriv oversikt
    N?r bruker gir denne ordren skal programmet skrive ut en oversikt over alle hyblene, som viser for hver hybel: hybelnavn, leietager-navn, og saldo.  Dersom hybelen er ledig, skal teksten LEDIG skrives ut i stedet for leietager-navn i oversikten, og saldoen skal vises som 0.  Til slutt skal n?v?rende m?ned, ?r, totalfortjeneste, og antall m?neder systemet har v?rt i drift skrives ut p? skjermen.  Eksempel p? hvordan oversikten kan se ut:
    Hybel Leietager               Saldo
    ----- --------------------- -------
    1A    Ole Einar                6000
    1B    Tora Berg               12000
    1C    ( LEDIG )                   0
    1D    Per Smart                9000
               ...osv...
    
    M?ned/?r:          3/2010
    Totalfortjeneste:  1400500 etter 24 m?neder
    

  2. Registrer ny leietager
    Denne ordren brukes n?r en student ?nsker ? flytte inn og leie en av hyblene.  F?rst skal systemet sjekke om det finnes ledige hybler.  Hvis det ikke er noen ledige hybler skal programmet bare skrive ut en melding om det og returnere til hovedmenyen.

    Hvis det finnes ledige hybler skal hybelnavnene til disse (f.eks 1C, 2B) skrives ut p? skjermen, og s? skal programmet sp?rre hvilken hybel studenten ?nsker ? leie (se hint 5 under for hvordan slike hybelnavn kan leses fra tastatur).  Er valgt hybel ledig, skal programmet sp?rre om studentens navn, og registrere innflyttingen.

    Studenten som flytter inn betaler et depositum p? 10 000 kroner.  Fra dette trekkes det med én gang m?nedsleien for den f?rste m?neden (husk at m?nedsleien er avhengig av etasje).  Det som er til ?vers blir studentens saldo.  For enkelhets skyld trekkes det alltid én hel m?nedshusleie ved innflytting, uansett hvilken dag i m?neden studenten flyttet inn.

    Programmet skal til slutt skrive ut en beskjed p? skjermen om at innflyttingen ble gjennomf?rt.  Beskjeden skal inneholde hybelnavnet (etasje+bokstav), studentens navn, og saldo.

  3. Registrer betaling fra leietager
    Programmet skal sp?rre om et hybelnavn (f.eks. 2B), og bel?pet som beboeren i den oppgitte hybelen skal betale.  Bel?pet adderes til studentens saldo og en passende melding skrives p? skjermen.  Hvis hybelen som ble oppgitt ikke hadde beboer, skal det skrives en feilmelding.  Saldoen fungerer som en slags konto som studenten har hos Gulbrand, og hver student m? passe p? ? ha nok penger inne i denne saldoen til ? dekke husleiene hver m?ned.

  4. Registrer frivillig utflytting
    Programmet sp?r om navnet p? studenten som ?nsker ? flytte ut.  Deretter g?r programmet gjennom alle hyblene og ser om det finner en student med angitt navn.  Hvis studenten ikke ble funnet, skal det gis en feilmelding.

    Blir studenten funnet, skal du registrere i systemet at hybelen ikke lenger har beboer (du kan f.eks. angi dette vha. null-peker eller bruke det spesielle navnet "TOM HYBEL", men det du velger m? selvsagt fungere sammen med resten av programmet ditt).  Studenten blir trukket et gebyr p? 650 kroner for utflyttingen, som du legger til Gulbrands totale fortjeneste, og resten av saldoen f?r studenten tilbake (hvis den var p? mer enn 650 kr).  Skriv ut p? skjerm resultatet av ? trekke utflyttingsgebyret fra saldoen.  Hvis studenten hadde mindre enn 650 kr. igjen i saldoen antar vi for enkelhets skyld at hun ogs? betaler dette restbel?pet n? samtidig med utflyttingen.

    [ Valgfri ekstra-oppgave: Hvis du ?nsker det kan du f.eks. utvide denne ordren til ? vise en meny med student-navnene sortert alfabetisk, slik at det blir lettere for Gulbrand ? velge den som skal flytte ut. ]

  5. M?nedskj?ring av husleie
    Meningen er at Gulbrand skal utf?re denne ordren f?rste dag i hver m?ned.  N?r ordren velges skal programmet f?rst sp?rre om brukeren virkelig ?nsker ? utf?re m?nedskj?ringen, for m?neden som kommer etter m?neds- og ?rstallet da m?nedskj?ring sist ble utf?rt (f.eks. hvis siste m?nedskj?ring ble utf?rt for m?ned 3 i ?r 2010, kan sp?rsm?let v?re "?nsker du ? utf?re m?nedskj?ring for m?ned 4/2010?").  Svarer brukeren nei, returnerer programmet til hovedmenyen.

    Svarer brukeren ja, skal m?nedsnummeret (og ved ?rsskiftet ?rstallet ogs?) oppdateres, og det utf?res det m?nedlige regnskapet.  Programmet g?r gjennom alle hyblene: For hver hybel trekkes (Gulbrands) utgifter fra (Gulbrands) fortjeneste - husk ? trekke fra kostnader for fellesarealet ogs?.  For hyblene som har beboer trekkes m?nedsleien for hybelen fra studentens saldo og legges til Gulbrands fortjeneste.  Gulbrand belaster alts? husleien forskuddsvis, for m?neden som nettopp har begynt.  Hvis noen leietagere ikke hadde nok i saldoen s? trekkes husleien likevel, og saldoen g?r da i minus (Gulbrand har uansett en god l?sning p? slike tilfeller, se menyvalg 6).

    Til slutt skal f?lgende skrives til skjerm:

    1. M?ned/?r som m?nedskj?ringen gjelder for.
    2. M?nedens fortjeneste (dvs. Gulbrands inntekter minus utgifter i denne m?nedskj?ringen).
    3. Gulbrands nye totalfortjeneste (oppdatert med denne m?nedens), antall m?neder systemet har v?rt i drift (?kt med én), og gjennomsnittlig m?nedsfortjeneste (dvs. resultatet av ? dele totalfortjeneste / totaltAntallM?neder).
    4. N?v?rende ?fyllingsgrad? for Utsyn, angitt vha. antall n?v?rende leietagere, og prosenten det utgj?r av de 18 hyblene f.eks. ?16 Av 18 hybler i bruk (Fyllingsgrad 89%)?.
    5. [ Valgfri ekstra-oppgave: Hvis du ?nsker det kan du programmere utskrift av ?historisk fyllingsgrad?, omtrent som i punkt ?d.?, men beregnet for hele drifts-tiden til systemet, ved ? anta at at utgiftene og husleiene har v?rt de samme siden starten, og at forekomstene av ledige hybler har v?rt jevnt fordelt i perioden, med 2/3 av disse i etasje 1?2, og 1/3 i toppetasjen.  Anta 1 utflytting og 1 innflytting pr. m?ned i gjennomsnitt, 1 utkasting pr. ?r, og at det var gjennomsnittlig antall beboere i hybelhuset da systemet ble satt i drift.  Er fyllingsgrad i n?v?rende m?ned bedre enn gjennomsnittet? ]

  6. Kast ut leietagere
    Hybelhuset til Gulbrand er veldig popul?r, med mange studenter som ?nsker ? leie.  For ? ?pne plass for nye leietagere (og sikre inntektene sine) s? har han innf?rt en regel om at leietagere som har kommet s? langt i minus p? saldoen at de skylder mer enn én husleie, blir kastet ut hva. torpedo n?r denne ordren utf?res.  Kravet til hver enkelt student som kastes ut er det de skylder i husleie pluss et torpederingsgebyr p? 3000 kroner.  Torpedoen og Guldbrand deler dette begyret likt.  Gulbrands halvdel av gebyret legges til totalfortjenesten med én gang n?r dette menyvalget kj?res (siden hjelperen hans alltid greier ? ordne disse sakene).

    Programmet g?r gjennom alle hyblene og finner studentene med saldo enda lavere enn én m?nedsleie i minus (husk de forskjellige leiepriser!).  For hver av disse studentene skal du kalle f?lgende hjelpemetode: (som du ogs? skal programmere)

    void tilkallTorpedo(int etasje, int rom, int krav) { // ...
    
    Denne metoden skriver hybelnavn (etasje+rom), studentens navn, og pengekravet, b?de til skjerm og til en fil kalt torpedo.txt.  N?r metoden skriver til filen, skal den ikke overskrive det som ligger der, men bare legge til en ny linje p? slutten (se hint 6 nedenfor).

  7. Avslutt
    Ved utf?relse av denne ordren skal n?dvendige data skrives til hybeldata.txt: m?ned, ?r, totalfortjeneste, antall m?neder i drift, samt hybelstatus for alle hyblene.  Deretter skal programmet avslutte.

Hint og hjelp til oppgaven

  1. Det st?r i leveringskrav ovenfor at du m? ha minst 4 klasser: Oblig3, Utsyn, Hybel og Student.  Det kan argumenteres for at Student er overfl?dig siden vi bare vet to ting om hver student: navn og saldo.  Det er likevel fornuftig ? ha med denne klassen, i tilfelle Gulbrand ?nsker ? utvide systemet med mer informasjon om hver student, som f.eks. mobilnummer, innflyttingsm?ned o.l.

    Nedenfor er det et programskjelett du kan bruke.  Du kan utvide det med flere metoder og eventuelt andre klasser.  M?nsterbesvarelsen er p? ca. 400 linjer.

    // Skriv et kommentar om programmet ditt her: ...
    import easyIO.*;
    
    class Oblig3 {
        public static void main(String [] args) {
            Utsyn s = new Utsyn();
            s.ordreL?kke();
        }
    }
    
    class Student {
        String navn; // studentens navn
        int saldo;   // studentens saldo
    
        // Evt. metoder for ? behandle en Student...
    }
    
    class Hybel {
        Student leietager;  // peker p? et Student-objekt
        int husleie; // 6000 hvis hybelen er i 3. etasje, ellers 5000.
    
        // Evt. metoder for ? behandle en Hybel...
    }
    
    class Utsyn {
        Hybel[][] hybler = new Hybel[3][6];
    
        In tast = new In();
        Out skjerm = new Out();
        String DATAFIL = "hybeldata.txt";
    
        // Evt. variabler for ?konomidata...
    
        // Konstrukt?r for klassen Utsyn
        Utsyn() {
    	// Her kan du lese datafilen "hybeldata.txt" og lagre hele innholdet
    	// i datastrukturene dine.  Husk ? opprette Hybel- og Student-objekter.
    	// Eksempel p? innlesing av en saldo, for en gitt "etg" og "rom":
    	//    hybler[etg][rom] = new Hybel();
    	//    hybler[etg][rom].leietager = new Student();
    	//    hybler[etg][rom].leietager.saldo = innfil.inInt(" ;");
        }
    
        void ordreL?kke() {
            int ordre = -1;
            while (ordre != 7) {
                // Skriv ut meny:
                skjerm.outln("Meny:");
                skjerm.outln("1. ...");
    
                // Les ordre fra bruker
                skjerm.out("Ordre: ");
                ordre = tast.inInt();
    
                switch(ordre) {
                    case 1: skrivOversikt(); break;
                    case 2: registrerNyLeietager(); break;
                    case 3: registrerBetaling(); break;
                    case 4: // ...
                    // ... Fyll ut resten...
                    default: // Gi feilmelding.
                }
            }
        }
    
        // Metoder for de forskjellige ordrene i ordreL?kke()
    
        void skrivOversikt() { /* ... */ }
        void registrerNyLeietager() { /* ... */ }
        void registrerBetaling() { /* ... */ }
    
        // ... Fyll ut med minst 5 metoder til
    }
    

  2. I klassen Utsyn er det en metode som ikke har det reserverte ordet void eller noe annet foran metodenavnet, og heter det samme som klassen: ?Utsyn()?.  Det er en spesiell type metode kalt en konstrukt?r, som blir utf?rt automatisk n?r man oppretter et objekt av klassen (med new Klassenavn()).  Du kan lese mer om konstrukt?rer i avsnitt 8.7 og 8.11 i l?reboken, eller i lysarkene fra uke 7, eller i Marit Nybakkens notat ?konstrukt?rer.pdf?.

    Det anbefales ? starte arbeidet med obligen ved ? programmere konstrukt?ren Utsyn() slik at den leser datafilen.  I begynnelsen b?r du ogs? skrive ut p? skjermen det som du leste inn fra fil, slik at du kan kontrollere at datafilen blir lest riktig, f.eks. i stedet for bare ? si ?int x = fil.inInt(" ;");? kan du bruke:

    int x = fil.inInt(" ;");  skjerm.outln("x=" + x);
    

  3. Objektvariabelen leietager i klassen Hybel skal kunne peke p? Student-objekter.  Du kan teste om en peker-variabel som leietager faktisk peker p? et eksisterende Student-objekt ved et gitt tidspunkt slik:
    if (leietager != null)
    ?null? er et reservert ord i Java som angir peker-verdien ?intet objekt?.  if-testen ovenfor sjekker derfor om leietager peker p? noe som helst objekt annet enn ?intet objekt?.  Dette er nyttig hvis du velger ? angi tomme hybler vha. null-peker (se menyvalg 4).  Omvendt kan du ogs? sjekke om en peker ikke peker p? noe objekt, vha. == null.  For ? sette en peker til ? peke p? null, f.eks. n?r en student flytter ut, kan du skrive:
    leietager = null;

  4. N?r man bruker ?prikk-notasjon? p? en peker (f.eks. peker.saldo) m? man passe p? at den ikke inneholder null-verdien, ellers kr?sjer programmet og Java-kj?resystemet gir feilmeldingen NullPointerException.  F.eks, hvis du ?nsker ? f? tak i leietager.navn s? m? du passe p? at leietager peker p? noe annet enn null.  Dette kan ordnes vha. den f?rste if-testen vist i hint 3 ovenfor, eller ved ? kombinere den med en annen betingelse i en eksisterende if-test vha. &&.  Pass p? at testen p? != null st?r f?rst i den sammensatte if-betingelsen, f.eks. slik:
    Student s = hybler[i][j].leietager; // s (og leietager) kan v?re null
    if (s != null && s.navn.equals("Ola")) {
       // hit kommer vi hvis s ikke er null og s.navn er Ola
    }
    
    Flere tips til NullPointerException og andre feilmeldinger kan du finne i Ukeoppgaver 7.

  5. Ofte skal vi lese eller skrive ut hybelnavn (som 2B, 3E).  De best?r av et tall og en bokstav, men datastrukturen vi bruker for hybler er en array, som m? indekseres med tall; derfor m? vi konvertere bokstaven fra char til int.  Det kan gj?res hva. ?(int) (bokstav - 'A');? s? for ? lese et helt hybelnavn kan vi skrive:
    skjerm.out("Oppgi hybelnavn: ");
    int etg = (int) (tast.inChar(" \n\r") - '1'; // Etasje 1 -> [0] i arrayen
    char bokstav = tast.inChar(" \n\r"); // Les rombokstaven
    int rom = (int) (bokstav - 'A'); // 'A' gir [0], 'B' gir [1], osv.
    
    Det som st?r i parenteser etter inChar(...her...) angir tegn som innlesingen skal hoppe over, dvs. skal bare betraktes som skilletegn mellom de ?nskede input-data hvis de blir lest inn.  I koden ovenfor er det angitt at mellomrom (" "), linjeskift (\n), og vognretur (\r) skal betraktes som skilletegn av inChar.  Etter disse setningene vil variabelen rom inneholde verdien 0 hvis brukeren anga en A-hybel (f.eks. 3A), 1 hvis det var en B-hybel, osv.; og etasjenummer vil havne i variabelen etg (fratrukket 1).

    Omvendt kan man konvertere fra int til char, slik:

    char bokstav = (char) ('A' + intVariabel);
    

  6. Hver gang metoden tilkallTorpedo() blir kalt skal du skrive til slutten av filen torpedo.txt (uten ? overskrive det som allerede ligger der).  Da b?r man ?pne filen i append-modus, som s?rger for nettopp det (utskrift som sendes til filen blir da bare lagt til p? slutten av filen uten ? endre linjene den hadde fra f?r).  Det gj?res ved ? angi en ekstra-parameter ?,true? n?r man ?pner filen:
    Out fil = new Out("torpedo.txt", true);
    
    Husk ? lukke filer n?r du er ferdig med ? lese/skrive til dem! (fil.close()).

  7. I denne obligatoriske oppgaven skal du ved oppstart lese inn datafilen hybeldata.txt.  Hvis filen ikke finnes i samme mappe der du kj?rer programmet, kan programmet ditt kr?sje.  Dette kan du unng? ved ? lage datafilen manuelt, eller laste ned eksempel-datafilen, eller du kan skrive programmet ditt slik at det kan teste om filen finnes, og ta h?yde for det hvis ikke.  F?lgende kode kan brukes til denne testen, og krever at man har setningen import java.io.*; ?verst i programmet:
    if (new File("hybeldata.txt").exists()) {
       // les inn filen
    } else {
       // filen finnes ikke! Opprett hyblene uten studenter
    }