You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 7 Next »

Gender-klassen (vedlegg 1) representerer kjønnet til en person, i denne oppgaven begrenset til mann og kvinne. Klassen skal kodes slik at det ikke skal være mulig å ha andre Gender-objekter enn disse to kjønnene. En Gender-instans har et forklarende ord/navn knyttet til seg, som settes når instansen lages, og som ikke skal være direkte tilgjengelig fra andre klasser.

Oppgave a)

Hvilke modifikatorer bør stå foran deklarasjonen av label-feltet og konstruktøren? Begrunn svaret. 

  Begge disse bør være markert som private, siden de ikke skal være tilgjengelig utenfor klassen. label bør også være final, siden den ikke skal kunne endres.

Oppgave b)

Hvordan er klassen kodet så det forklarende ordet/navnet vises ved utskrift, f.eks. med System.out.println(...)?

Ved utskrift så brukes implisitt toString()-metoden, som er implementert og returnerer label.
Oppgave c)

Fullfør valueOf-metoden.

Her er det viktig å returnere et eksisterende Gender-objekt, ved å sammenligne argumentet med label-verdien i de to konstantene MALE og FEMALE. En kan også bruke switch, som i Java 8 virker med String-objekter. valueOf ligner på Integer.valueOf og Double.valueOf og må være static, siden det er unaturlig og unødvendig å kalle den på en eksisterende instans.

public static Gender valueOf(String label) {
   if (MALE.label.equals(label)) {
      return MALE;
   } else if (FEMALE.label.equals(label)) {
      return FEMALE;
   }
   return null;
}

Person-klassen (vedlegg 1) representerer en person, med et navn (String) og et kjønn (Gender). Navnet settes kun ved opprettelsen, mens kjønnet kan settes når som helst.

For å håndtere familieforhold så har et Person-objekt data om mor, far og barn. addChild-metoden brukes for å knytte et barn til en forelder, og det er ikke spesifisert andre metoder som endrer barn-forelder-koblingen. addChild både registrerer barnet og setter barnets kobling til mor eller far, avhengig av kjønnet til forelderen. Ta f.eks. kallet chris.addChild(pat), hvor pat blir registert som barnet til chris. Hvis chris er mann, så blir han registrert som pat sin far, mens hvis chris er kvinne, så blir hun registrert som pat sin mor.

Oppgave d)

Skriv felt, metoder og konstruktør for innkapsling av navn, kjønn, mor, og far. Bruk name, gender, mother og father som grunnlag for navngiving.

Her er litt av poenget at en må ha en konstruktør som setter navnet og utelate set-metoder for name, mother og father.

private final String name;
private Gender gender = null;
private Person father, mother;

public Person(String name) {
   this.name = name;
}


public String getName() {
   return name;
}

public Gender getGender() {
   return gender;
}

public void setGender(Gender gender) {
   this.gender = gender;
}

public Person getMother() {
   return mother;
}

public Person getFather() {
   return father;
}
Oppgave e)

Implementer getChildCount, hasChild og getChildren, med nødvendig(e) felt.

Her må en ha riktig deklarasjon av feltet (helst Collection, sekundært List), og huske å initialisere feltet. getChildren må passe på å returnere en ny Collection, så annen kode ikke får tilgang til interne data. En må ta høyde for at gender kan være null. En kan gjerne bruke Stream-teknikken.

 

private Collection<Person> children = new ArrayList<>();

public int getChildCount() {
   return children.size();
}

public boolean hasChild(Person child) {
   return children.contains(child);
}

// alternativ

public boolean hasChild(Person child) {
   return child.getFather() == this || child.getMother() == this;
}

public Collection<Person> getChildren(Gender gender) {
   Collection<Person> result = new ArrayList<>();
   for (Person child : children) {
      if (gender == null || child.getGender() == gender) {
         result.add(child);
      }
   }
   return result;
}
Oppgave f)

Som en del av innkapslingen av barn-koblingen, så har vi valgt å la Person implementere Iterable<Person>-grensesnittet. Hva betyr dette for koden en kan skrive for å gå gjennom barna til en Person? Implementer metoden(e) som er påkrevd av dette grensesnittet.

Hvis en klasse implementerer Iterable, så kan referanser til denne klassen brukes på høyresiden av kolonet i en for-each-løkke, f.eks. for (Person child : person). Se også getChildren-koden over.

Et (litt mindre relevant, og ikke påkrevd) alternativ er Iterable.forEach(Consumer<Person>). Dette er en såkalt default-metode (kom ikke frem i vedlegget) som en får gratis når en implementerer Iterable.

@Override
public Iterator<Person> iterator() {
   return children.iterator();
}
Oppgave g)

Implementer addChild-metoden, basert på kravene beskrevet over og i vedlegg 1, og kravene som implisitt testes av testAddChild i PersonTest-klassen (vedlegg 1).

 

 

public void addChild(Person child) {
   if (getGender() == Gender.MALE) {
      if (child.father != null) {
         child.father.children.remove(child);
      }
      child.father = this;
   } else if (getGender() == Gender.FEMALE) {
      if (child.mother != null) {
         child.mother.children.remove(child);
      }
      child.mother = this;
   }
   children.add(child);
}

Oppgave h)

Nederst i testmetoden testAddChild er to linjer marker med // ??? Hvilke tilfeller eller problemer er det som kan testes på de to punktene i koden? Hvordan oppfører din kode seg, og hva mener du er riktig oppførsel i de to tilfellene? (Du trenger ikke rette på koden din)

Her er det vanskelig å komme med en fasit, men poenget er å vurdere oppførselen til egen kode for tilfeller en ikke har tenkt på. En bør kunne beskrive de to tilfellene og forstå hva som faktisk vil skje i egen kode.

I det første tilfellet skifter far først kjønn, før hun på ny får lagt til barnet. Koden over vil fjerne koblingen til og fra den eksisterende moren, og så etablere en tilsvarende kobling med den nye moren, som også er den eksisterende faren. Hun ender opp med å være far og mor til samme barn, og dette barnet vil finnes dobbelt opp i children-lista. Her ville nok det riktigste være at den eksisterende far-koblingen også ble fjernet, før mor-barn-koblingen ble etablert.

I det andre tilfellet så legges faren til som barn av eget barn, som gir en sirkulær struktur. Dette bør det være en sjekk for, for mange algoritmer vil da ende opp i evig løkke.

Oppgave a)

 

 

Oppgave b)

 

 
Oppgave c)

 

 
Oppgave d)

 

 
Oppgave e)

 

 
Oppgave f)

 

 
Oppgave a)

 

 

Oppgave b)

 

 
Oppgave c)

 

 
Oppgave d)

 

 
Oppgave e)

 

 
Oppgave f)

 

 
Oppgave a)

 

 

Oppgave b)

 

 
Oppgave c)

 

 
Oppgave d)

 

 
Oppgave e)

 

 
Oppgave f)

 

 
/**
 * 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) {
      ...
   }
}

The format is line-based, with three kinds of lines:

  1. A line with a gender label followed by a name (in quotes) indicates a new person with that gender and name.
  2. Otherwise the line is a sequence of names (in quotes) of persons, the first being the parent of the others (i.e. children).
  3. Empty, only whitespace or starting with #. Such lines are ignored when loading.

Lines of all types can be mixed, but there should be no forward references from lines of type 2 to lines of type 1. I.e. the names in lines of type 2 have previously occurred in lines of type 1. Below are two examples of the format.

Example 1:

# all persons
# should result in a sequence of calls to Family's addMember method
male "Hallvard Trætteberg"
female "Marit Reitan"
male "Jens Reitan Trætteberg"
female "Anne Trætteberg Reitan"
# all mother/father-child relations
# should result in a sequence of calls to Person's addChild method
"Hallvard Trætteberg" "Jens Reitan Trætteberg" "Anne Trætteberg Reitan"
"Marit Reitan" "Jens Reitan Trætteberg" "Anne Trætteberg Reitan"

Example 2:

male        "Hallvard Trætteberg"
male "Jens Reitan Trætteberg"
female "Anne Trætteberg Reitan"
"Hallvard Trætteberg" "Jens Reitan Trætteberg" "Anne Trætteberg Reitan"
female      "Marit Reitan"
"Marit Reitan" "Jens Reitan Trætteberg" "Anne Trætteberg Reitan"
  • No labels