Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Expand
titleDel 2 -Family-klassen og IO (25%)

Family-klassen (vedlegg 1) holder rede på personer i en familie, med metoder for å legge til familiemedlemmer (Person-objekter), slå opp personer på navn, lagre familiemedlemmene og lese dem inn igjen.

Oppgave a)

Skriv metodene addMember og findMember (og definer nødvendige felt), som henholdsvis legger en Person til som familiemedlem og finner et familiemedlem med et angitt navn.

Expand
titleLF

Samme krav til members-lista (navnet er ikke nøye) som til Person.children. En trenger ikke sørge for at alle barn av personer som legges til, også legges til. Dette kan en anta gjøres utenfra. Må bruke equals for å sammenligne String-objekter.

Code Block
private Collection<Person> members = new ArrayList<>();

public void addMember(Person person) {
   members.add(person);
}

public Person findMember(String name) {
   for (Person person : members) {
      if (person.getName().equals(name)) {
         return person;
      }
   }
   return null;
}

I vedlegg 2 beskrives et tekstformat for data om personene i en familie, inkludert foreldre-barn-koblingene.

Oppgave b)

I vedlegg 1 er det med en hjelpemetode tokenize, som kan være nyttig ved innlesing og som kan antas ferdig implementert. Hvilke(n) modifikator(er) burde den ha? Begrunn svaret!

Expand
titleLF
En slik hjelpemetode bør for det første være markert som private, siden det ikke er naturlig at dette er en tjeneste som tilbys andre klasser. For det andre bør den være markert som static, siden den ikke bruker (leser eller endrer) tilstanden til noe Family-objekt. Eneste grunn til at den ikke skal være static, er hvis en subklasse av Family har behov for å redefinere den, og det er ikke aktuelt her.
Oppgave c)

Skriv metodene save og load, som støtter dette tekstformatet. Begrunn hvordan du velger å behandle problemet med unntak.

Expand
titleLF

Det viktigste med save-metoden er at den først skriver ut alle linjer av type 1, altså den person-informasjonen som er nødvendig for å lage Person-objektene før foreldre-barn-koblingen etableres. Vi velger å lage en PrintWriter rundt OutputStream-en vi får inn, for å muliggjøre bruke av print og println. Vi kunne brukt en PrintStream, men en Writer anbefales jo for tekst (trekker ikke for bruk av PrintStream). Så skrives alle linjene av type 2 ut. Derfor blir det to iterasjoner over alle medlemmene. Navn får anførselstegn (”) rundt (merk måten ” inkluderes i en String). Her sjekkes det om en person har barn (kan gjøre på mange måter), så det ikke blir linjer med en forelder, men det er strengt tatt ikke definert som et krav (det står ”sequence of names”, og en sekvens kan jo ha bare ett element). Hvis en har linjer med bare én forelder, så er det viktig at load-metoden håndterer det riktig. Det er vanlig at den som setter opp en OutputStream også lukker den, og derfor avslutter vi ikke med pw.close(). Vi avslutter imidlertid med pw.flush() for å sikre at all vår output sendes ut med en gang (trekker ikke for manglende bruk av close()/flush()).

load-metoden klassifiserer hver linje som en av de tre typene ved å først sjekke om den er tom eller starter med # (type 3) og så sjekker om første token i en linje er en gyldig Gender (type 1). Ellers er den av type 2. Her gjøres det ingen sjekk på om formatet er korrekt, f.eks. om et barn i en linje av type 2 faktisk er registrert som familiemedlem. Det er kanskje litt uklart hvorvidt og evt. hvordan tokenize håndterer #, så det er greit at den brukes før en sjekker for linjer av type 3.

Unntak håndteres ikke av metodene, så de må deklareres med throws. Det er naturlig å bruke IOException, for den utløses ved bruk av InputStream og OutputStream. En kunne brukt Exception or å markere (at vi er klar over) at det er mye som kan gå galt, men det anbefales å bruke den mest spesifikke typen. Vi kunne fanget opp og ignorert unntak, men det kan lett maskere feil vi ønsker å avdekke.

Code Block
private void outputQuotedName(Person person, PrintWriter pw) {
   pw.print("\"" + person.getName() + "\"");       
}

public void save(OutputStream out) throws IOException {
   PrintWriter pw = new PrintWriter(out);
   pw.println("# all persons");
   for (Person person : members) {
      pw.print(person.getGender());
      pw.print(" ");
      outputQuotedName(person, pw);
      pw.println();
   }
   pw.println();
   pw.println("# all mother/father-child relations");
   for (Person person : members) {
      if (person.iterator().hasNext()) {
         outputQuotedName(person, pw);
         for (Person child : person) {
            pw.print(" ");
            outputQuotedName(child, pw);
         }
         pw.println();
      }
   }
   pw.flush();
}

public void load(InputStream in) throws IOException {
   Scanner scanner = new Scanner(in);
   while (scanner.hasNextLine()) {
      String line = scanner.nextLine();
      if (line.trim().length() == 0 || line.startsWith("#")) {
         continue;
      }
      List<String> tokens = tokenize(line);
      Gender gender = Gender.valueOf(tokens.get(0));
      if (gender != null) {
         // type 1 line
         Person person = new Person(tokens.get(1));
         person.setGender(gender);
         addMember(person);
      } else {
         // type 2 line
         Person person = findMember(tokens.get(0));
         for (int i = 1; i < tokens.size(); i++) {
            Person child = findMember(tokens.get(i));
            person.addChild(child);
         }
      }
   }
   scanner.close();
}


Oppgave d)

 Tegn objektdiagram for objektstrukturen som er resultatet av å lese inn eksempelteksten i vedlegg 2 med load-metoden til et nyopprettet Family-objekt. Hvilke av objektene fantes fra før og hvilke er blitt opprettet av load-metoden?

Expand
titleLF
 
Oppgave e)

 

Expand
titleLF
 
Oppgave f)

 

Objektdiagram kan tegnes på mange måter. Det viktigste er å ha med navn med klassen til objektene, attributter med enkle verdier (inkl. String) inni boksen og piler med attributtnavn for referansetyper. Identitet er ikke så viktig her. Family- og Gender-objektene fantes fra før.

Image Added

Expand
titleLF
 

Expand
titleDel 3 - Familierelasjoner og Relation- <tema> implementasjoner (xx%)25%)

Denne deloppgaven handler om familierelasjoner, som forelder/far/mor, søsken/søster/bror, onkel/tante, søskenbarn/kusine/fetter, niese/nevø osv.

Sister-klassen skal implementere Relation-grensesnittet (vedlegg 1), slik at metoden getRelativesOf returnerer alle søstrene til Person-argumentet. new Sister().getRelativesOf(person) skal altså returnere alle søstrene til person. Tilsvarende skal klassen Parent implementere Relation slik at metoden getRelativesOf returnerer alle foreldrene, altså mor og/eller far, til Person-argumentet. Merk at Sister- og Parent-klassene ikke står i noe vedlegg.

Oppgave a)

Implementer Sister- og Parent-klassene. 

Expand
titleLF

Parent-klassen er enklest. En må få med både mor og far, og unngå å legge til null-verdier. I Sister-klassen er det laget en hjelpemetode kalt addChildren, som legger alle jente-barn av en gitt Person til en liste og unngår duplikater. Denne kalles med både mor og far til personen en ønsker å finne søstrene til. Merk at dette også vil legge til halvsøstre. Til slutt fjernes personen selv, for hun er jo ikke sin egen søster (subtilt poeng, jeg vet)!

Code Block
public class Sister implements Relation {


   private void addChildren(Person person, Collection<Person> result) {
      if (person != null) {
         for (Person child : person) {
            if (child.getGender() == Gender.FEMALE && (! result.contains(child))) {
                result.add(child);
            }
         }
      }
   }

   @Override
   public Collection<Person> getRelativesOf(Person person) {
          Collection<Person> result = new ArrayList<>();
          addChildren(person.getFather(), result);
          addChildren(person.getMother(), result);
          result.remove(person);
          return result;
    }
}

public class Parent implements Relation {


   @Override
   public Collection<Person> getRelativesOf(Person person) {
      Collection<Person> result = new ArrayList<>();
      if (person.getMother() != null) {
         result.add(person.getMother());
      }
      if (person.getFather() != null) {
         result.add(person.getFather());
      }
      return result;
   }
}
Oppgave a)

 

Expand
titleLF
 
Oppgave b)

 

Expand
titleLF
 
Oppgave c)

 

Expand
titleLF
 
Oppgave d)

 

Expand
titleLF
 
Oppgave e)

 

Expand
titleLF
 
Oppgave f)

 

Expand
titleLF
 
Expand
titleDel 4 - <tema> (xx%)
Oppgave a)

 

Expand
titleLF

 

Oppgave b)

 

Expand
titleLF
 
Oppgave c)

 

Expand
titleLF
 
Oppgave d)

 

Expand
titleLF
 
Oppgave e)

 

Expand
titleLF
 
Oppgave f)

 

Expand
titleLF
 
Expand
titleAppendix 1
Code Block
/**
 * This class represents the gender of a Person.
 * It cannot be instantiated outside this class.
 * It provides all legal Gender values as static variables.
 */
public class Gender {

   ... String label;

   ... Gender(String label) {
      this.label = label;
   }

   @Override
   public String toString() {
      return label;
   }

   public static Gender
      MALE = new Gender("male"),
      FEMALE = new Gender("female");

   /**
    * Returns a pre-existing Gender instance for the provided label, or
    * null of there is no such instance.
    * @param label
    * @return a pre-existing Gender instance
    */
   ... Gender valueOf(String label) {
         ...
   }
}

public class Person implements Iterable<Person> {

   ... fields for name, gender, mother and father ...

   ... constructor ...

   ... methods for name, gender, mother and father ...

   ... field(s) for children ...

   /**
    * @return the number of children of this Person
    */
   public int getChildCount() {
      ...
   }

   /**
    * @param child
    * @return if this Person has the provided Person as a child
    */
   public boolean hasChild(Person child) {
      ...
   }

   /**
    * Returns all children of this Person with the provided Gender.
    * If gender is null, all children are returned.
    * Can be used to get all daughters or sons of a person.
    * @param gender
    */
   public Collection<Person> getChildren(Gender gender) {
      ...
   }

   /**
    * Adds the provided Person as a child of this Person.
    * Also sets the child's father or mother to this Person,
    * depending on this Person's gender.
    * To ensure consistency, if the provided Person already
    * has a parent of that gender,
    * it is removed as a child of that parent.
    * @param child
    */
   public void addChild(Person child) {
      ...
   }
}

public class PersonTest extends TestCase {

   public void testAddChild() {
      Gender female = Gender.valueOf("female"), male = Gender.valueOf("male");
      Person mother = new Person("Chris"); mother.setGender(female);
      Person father1 = new Person("Pat");  father1.setGender(male);
      Person father2 = new Person("Alex"); father2.setGender(male);
      Person child = new Person("Jean");
      mother.addChild(child);
      assertEquals(1, mother.getChildCount());
      assertTrue(mother.hasChild(child));
      assertEquals(mother, child.getMother());
      mother.addChild(child);
      assertEquals(1, mother.getChildCount());

      father1.addChild(child);
      assertTrue(father1.hasChild(child));
      assertEquals(father1, child.getFather());

      father2.addChild(child);
      assertFalse(father1.hasChild(child));
      assertTrue(father2.hasChild(child));
      assertEquals(father2, child.getFather());

      father2.setGender(female);
      father2.addChild(child);
      // ???
      child.addChild(father2);
      // ???
   }
}

public class Family {
   /**
    * Adds a Person as a new family member
    * @param person the Person to add
    */
   public void addMember(Person person) {
      ...
   }

   /**
    * Finds a member with the given name
    * @param name
    * @return the Person in this Family with the provided name
    */

   public Person findMember(String name) {
      ...
   }

   //

   /**
    * Writes the contents of this Family to the OutputStream,
    * so it can be reconstructed using load.
    * @param out
    */
   public void save(OutputStream out) ... {
      ...
   }

   /**
    * Helper method that splits a line into a list of tokens,
    * either words or quoted names (quotes are removed).
    * @param line - the string to tokenize
    */
   ... List<String> tokenize(String line) {
      ... no need to implement this method ...
   }

   /**
    * Loads contents from the provided InputStream into this Family.
    * @param in
    */
   public void load(InputStream in) ... {
      ...
   }
}

public interface Relation {
   /*
    * Returns the Collection of Persons related to the provided Person
    * according to this Relation.
    * E.g. if this Relation corresponds to the concept of niece,
    * it should return all Persons that are nieces of person.
    */
   Collection<Person> getRelativesOf(Person person);
}

public class Relation2 implements Relation {

   public Relation2(Relation rel1, Relation rel2) {
      ...
   }

   @Override
   public Collection<Person> getRelativesOf(Person person) {
      ...
   }
}

...