Wie gefordert wurde die Aufgabe als lineare Liste implementiert. Die
Methode append(Entry)
fügt einen Eintrag am Ende der
Liste, die Methode prepend(Entry)
am Anfang der Liste an.
Diese beiden Methoden sind als private
deklariert und
werden nur (bei Bedarf) in der Methode insert(Entry)
aufgerufen, welche wiederum als public
deklariert ist und
zum (sortierten) Einfügen eines neuen Telefonbucheintrags vom
rufenden Prg. genutzt werden kann. Die Liste enthält
zusätzlich einen Verweis auf den letzten Eintrag der Liste, damit
in append(Entry)
beim Anfügen eines Eintrags am Ende
der Liste nicht erst die ganze Liste durchlaufen werden muss. Die
Methode insert(Entry)
sorgt auch dafür, dass das
Telefonbuch immer sortiert ist und bleibt.
Entry lookupEntry(String)
ermöglicht das Suchen
eines bestimmten Namen im Telefonbuch, sie nutzt die Methode Entry.compareNameTo(Entry)
,
wobei mit dem zu suchenden Namen ein temporäres Entry
-Objekt
erzeugt wird. Dies bewerkstelligte, dass der Code zum Vergleichen von
Namen (einmal zum Suchen, einmal in insert(Entry)
für
die Sortierung) nur einmal existiert (und implementiert werden musste).
Die möglichen Ergebnisse dieses Vergleichs wurden in static
final
Variablen (Entry.SMALLER
, Entry.GREATER
, Entry.EQUAL
)
gespeichert und können vom rufenden Prg. zur Vermeidung von
hardcodierten int
-Werten verwendet werden
Wird der Suchname nicht vollständig angeg., wird der (erste)
diesem Suchnamen am nächsten kommende Eintrag zurückgeg. (wie
in einem richtigen Handy-Telefonbuch). Existiert beispielsweise ein
Eintrag mit Namen "Daniel Brunthaler" als erster Eintrag mit den
Anfangsbuchstaben "Da" und ist der Suchname = "Da", wird dieser Name
zurückgeg.
Der Source enthält der Einfachheit halber (um den fast
identischen Source nicht zweimal anführen zu müssen) auch die
wenigen Erweiterungen für Aufgabe 2. Erläuterungen dazu und
der Source für den PhoneBookIndex
folgen in der
Lösungsidee und dem Source zu Aufgabe 2.
/** * Softwareentwicklung I - Exercise 10/1. * Phone book. * In-source comments are followed by the described code. * * @see Entry, PhoneBookIndex. * @author Daniel Brunthaler (0255054) */ public class PhoneBook { private Entry firstEntry = null; private Entry lastEntry = null; private PhoneBookIndex phoneBookIndex = new PhoneBookIndex(); /** * Constructs an empty PhoneBook. */ public PhoneBook() { super(); } public Entry getFirstEntry() { return firstEntry; } /** * Prepend the given Entry. * @param e Entry which should be prepended. */ private void prepend(Entry e) { if (firstEntry == null) { // empty PhoneBook => first Entry in list. firstEntry = e; lastEntry = e; } else { e.setNext(firstEntry); firstEntry = e; } phoneBookIndex.add(e); } /** * Append the given Entry. * @param e Entry which should be appended. */ private void append(Entry e) { e.setNext(null); // last Entry => no next Entry. if (firstEntry == null) { // empty PoneBook => first Entry in list. firstEntry = e; lastEntry = e; phoneBookIndex.add(e); } else { lastEntry.setNext(e); // append given node. if (lastEntry.getName().charAt(0) != e.getName().charAt(0)) phoneBookIndex.add(e); lastEntry = e; } } /** * Insert given Entry in the right place of (name) sorted PhoneBook that * the PhoneBook remains (name) sorted. * @param e Entry which should be inserted. */ public void insert(Entry e) { Entry before = null; Entry after = firstEntry; while (after != null) { if (e.compareNameTo(after) == Entry.SMALLER) { if (before == null) { // Entry is smaller than first Entry of PhoneBook // => prepend Entry. prepend(e); } else { before.setNext(e); e.setNext(after); if (before.getName().charAt(0) != e.getName().charAt(0)) phoneBookIndex.add(e); } return; } before = after; after = after.getNext(); } // Entry is bigger than last Entry of PhoneBook => append Entry. append(e); } /** * Search for given name in the PhoneBook. Returns the first Entry, which * name match the given name (at least beginning of the name). If no * matching name is found, null is returned. * @param name which should be searched. * @return Entry which name match the given name. */ public Entry lookupEntry(String name) { Entry temp = new Entry(name,null); Entry e = firstEntry; Entry before = null; while (e != null) { int compareResult = temp.compareNameTo(e); if (compareResult == Entry.EQUAL) { return e; } e = e.getNext(); } return null; } /** * First Entry which first character of name matches the given character. * If Entry not exists, the first existing Entry, which name consists of * a following character is returned. Null if no following entries exist. * @param sign first character * @return Entry which first character of name matches the given character. */ public Entry firstEntryWith(char sign) { return phoneBookIndex.firstEntryWith(sign); } /** * Test application for PhoneBook. * @param args */ public static void main(String[] args) { PhoneBook phoneBook = new PhoneBook(); Entry next = null, searchResult = null; String prompt = "Bitte Operation auswählen (i,f,n,s,c,t,q): "; String name, telNumber; char input, sign; IO.writeLn("Telefonbuch"); IO.writeLn("===========\n"); IO.writeLn("Folgende Operationen stehen zur Verfügung:"); IO.writeLn(" i - insert: Einfügen eines Eintrags"); IO.writeLn(" f - first: Ersten Eintrag anzeigen"); IO.writeLn(" n - next: Nächsten Eintrag anzeigen"); IO.writeLn(" s - search: Einen Eintrag nach Namen nachschlagen"); IO.writeLn(" c - character: Ersten Eintrag mit bestimmten "+ "Buchstaben anzeigen"); IO.writeLn(" t - test: Testeinträge automatisch erzeugen"); IO.writeLn(" q - quit: Programm verlassen\n"); IO.write(prompt); input = IO.readLine().trim().toLowerCase().charAt(0); while (input != 'q') { switch (input) { case 'i': IO.write(" Name? "); name = IO.readLine(); IO.write(" Telefonnummer? "); telNumber = IO.readLine(); if (name.trim().length() > 0) phoneBook.insert(new Entry(name,telNumber)); else IO.writeLn(" Name ungültig!"); break; case 'f': if (phoneBook.getFirstEntry() == null) { IO.writeLn(" Telefonbuch leer!"); } else { IO.writeLn("--> " + phoneBook.getFirstEntry().toString()); next = phoneBook.getFirstEntry().getNext(); } break; case 'n': if (next == null) { IO.writeLn(" Ende des Telefonbuchs erreicht!"); } else { IO.writeLn("--> " + next.toString()); next = next.getNext(); } break; case 's': IO.write(" Name? "); name = IO.readLine().trim(); searchResult = phoneBook.lookupEntry(name); if (searchResult == null) { IO.writeLn(" Keine passenden Einträge gefunden!"); } else { IO.writeLn("--> " + searchResult.toString()); } break; case 'c': if (phoneBook.getFirstEntry() == null) { IO.writeLn(" Telefonbuch leer!"); } else { IO.write(" Buchstabe? "); sign = IO.readLine().trim().toLowerCase().charAt(0); searchResult = phoneBook.firstEntryWith(sign); if (searchResult == null) { IO.writeLn(" Keine passenden Einträge gefunden!"); } else { IO.writeLn("--> " + searchResult.toString()); } } break; case 't': phoneBook.insert(new Entry( "Anton Muster","+43 (732) 1111")); phoneBook.insert(new Entry( "Bertold Brecht","+43 (6333) 2222")); phoneBook.insert(new Entry("Daniel","+43 650 31200000")); phoneBook.insert(new Entry( "Daniel Home","+43 (6225) 3210")); phoneBook.insert(new Entry("Hubert","12345678")); phoneBook.insert(new Entry("Zenzi","+43 664 54321")); IO.writeLn(" Testeinträge erzeugt"); break; default: IO.writeLn(" Unbekanntes Kommando!"); break; } IO.write("\n" + prompt); input = IO.readLine().trim().toLowerCase().charAt(0); } } }
/** * Softwareentwicklung I - Exercise 10/1. * Entry for PhoneBook. * In-source comments are followed by the described code. * * @see PhoneBook. * @author Daniel Brunthaler (0255054) */ public class Entry { public static final int SMALLER = -1; public static final int EQUAL = 0; public static final int GREATER = 1; private Entry next = null; private String name = null; private String telNumber = null; /** * Constructs an Entry. * @param name of contact. * @param telNumber telephone number of contact. */ public Entry(String name,String telNumber) { super(); this.name = name.trim(); if (telNumber == null) this.telNumber = "not entered"; else this.telNumber = telNumber.trim(); } /** * Get Entry following this Entry. * @return next Entry following this Entry. */ public Entry getNext() { return this.next; } /** * Set following Entry. * @param e Entry which should be set as following Entry. */ public void setNext(Entry e) { this.next = e; } /** * Get name of this Entry. * @return name of Entry. */ public String getName() { return this.name; } /** * Telephone number of this Entry. * @return telephone number of Entry. */ public String getTelNumber() { return this.telNumber; } /** * Compare name of this Entry to the name of another Entry. * @param e Entry which should this Entry compared with. * @return Entry.SMALLER, Entry.GREATER or Entry.EQUAL. */ public int compareNameTo(Entry e) { String name1 = this.getName().toLowerCase(); String name2 = e.getName().toLowerCase(); int length = name1.length(); int intValue1,intValue2; if (name2.length() < length) { int numberOfSpacesToAdd = length - name2.length(); for (int i = 0;i < numberOfSpacesToAdd;i++) name2 += " "; } for (int i = 0;i < length;i++) { intValue1 = Character.getNumericValue(name1.charAt(i)); intValue2 = Character.getNumericValue(name2.charAt(i)); if (intValue1 < intValue2) { return SMALLER; } else if (intValue1 > intValue2) { return GREATER; } } return EQUAL; } /** * Adequate String of this Entry. * @return ", ". */ public String toString() { return name + ", " + telNumber; } }
Der Test wurde in der main
-Methode implementiert
(siehe Source). Es wurde praktisch der in der Aufgabenstellung
angeführte Beispieldialog realisiert. Um das Testen zu
vereinfachen, wurde die Testapplikation um die Fkt. "t
-Testeinträge automatisch erzeugen" erweitert. Diese Fkt. erzeugt
automatisch die in der main
-Methode ersichtlichen
Testeinträge im Telefonbuch. Die Testapplikation enthält auch
schon die Fkt. "c", welche aber erst in der Aufgabe 2 zum Einsatz kommt.
Testplan
Eingabe |
Erwartete Ausgabe |
Zweck |
---|---|---|
i - Eintrag "Markus ..." erzeugen |
- |
Einen Eintrag manuell erzeugen |
f - Ersten Eintrag anzeigen |
"Markus ..." |
Wurde erfasster Eintrag richtig
gespeichert? |
t - Testeinträge erzeugen |
- |
Telefonbuch mit Einträgen
füllen |
f - Ersten Eintrag anzeigen |
"Anton ..." |
Wurde Telefonbuch richtig
befüllt? Ist es noch sortiert? |
n - Nächsten Eintrag
anzeigen |
"Bertold ..." |
Einträge und Sortierung
überprüfen |
n - Nächsten Eintrag
anzeigen |
"Daniel ..." |
Einträge und Sortierung überprüfen |
... |
... |
... |
n - Nächsten Eintrag
anzeigen |
Ende der Liste erreicht |
Ende der Liste |
s - "Da" suchen |
"Daniel ..." |
Eintrag suchen |
s - "Daniel H" suchen |
"Daniel Home ..." |
Auf "Daniel" folgenden Eintrag
suchen |
s - "Z" suchen |
"Zenzi ..." |
Letzten Eintrag suchen |
q - Programm beenden |
- |
Programm beenden |
Hardcopy
Die Klasse PhoneBookIndex
ist relativ kompakt
ausgefallen. Dadurch, dass jeder Anfangsbuchstabe einfach und eindeutig
einem Index im Anfangsbuchstaben-Array zugeordnet werden kann,
fällt die Implementation der Methode add(Entry)
sehr
kurz aus. In der Klasse PhoneBook
wurden einige wenige
Anpassungen vorgenommen, sodass beim Erzeugen eines Telefonbuch-Eintrags
immer auch ein entspr. Index im PhoneBookIndex
erzeugt wird
(siehe Source zu Aufgabe 1). Die Klasse Entry
blieb
unverändert.
Die Methode Entry firstEntryWith(char)
gibt den Eintrag,
wessen Name den übergebenen Anfangsbuchstaben hat, oder wenn ein
solcher nicht existiert, den nächsten folgenden Namen (Eintrag)
zurück. Existieren danach keine Namen (Einträge) mehr, wird null
zurückgeg.
/** * Softwareentwicklung I - Exercise 10/2. * PhoneBookIndex for PhoneBook. * In-source comments are followed by the described code. * * @see PhoneBook. * @author Daniel Brunthaler (0255054) */ public class PhoneBookIndex { private Entry[] firstCharIndex = new Entry[26]; /** * Constructs an empty PhoneBookIndex. */ public PhoneBookIndex() { super(); } /** * Adds an Entry to the index. * @param e Entry which should be added to the index. */ public void add(Entry e) { char firstChar = Character.toLowerCase(e.getName().charAt(0)); firstCharIndex[Character.getNumericValue(firstChar) - 10] = e; } /** * First Entry which first character of name matches the given character. * If Entry not exists, the first existing Entry, which name consists of * a following character is returned. Null if no following entries exist. * @param sign first character * @return Entry which first character of name matches the given character. */ public Entry firstEntryWith(char sign) { Entry e = null; sign = Character.toLowerCase(sign); for (int i = Character.getNumericValue(sign) - 10;i < 26;i++) { e = firstCharIndex[i]; if (e != null) return e; } return null; } }
Zum Testen wurde wieder die Fkt. "t - Testeinträge automatisch erzeugen" verwendet. Auf diesen Testeinträgen kann schließlich die Fkt. "c" angewendet werden. Vor dem Erzeugen der Testeinträge wird ein einzelner Eintrag erzeugt, um den Fall, dass kein "Folgename" existiert, zu testen.
Testplan
Eingabe |
Erwartete Ausgabe |
Zweck |
---|---|---|
i - Eintrag "Markus ..." erzeugen |
- |
Einen Eintrag manuell erzeugen |
c - Erster Eintrag mit Buchstabe "m" | "Markus ..." |
Wird erfasster Eintrag angezeigt? |
c - Erster Eintrag mit Buchstabe "n" | Nicht gefunden | Testfall - es existiert kein "Folgename" |
t - Testeinträge erzeugen | - | Testeinträge erzeugen |
c - Erster Eintrag mit Buchstabe "a" | "Anton ..." |
Wird richtiger Eintrag angezeigt? |
c - Erster Eintrag mit Buchstabe "d" | "Daniel ..." |
Wird richtiger Eintrag
angezeigt? (Diesmal exisiteren zwei Einträge mit Namen "D..." |
c - Erster Eintrag mit Buchstabe "e" | "Hubert ..." | Wird richtiger Folgename angezeigt? |
c - Erster Eintrag mit Buchstabe "n" | "Zenzi ..." | Wird richtiger Folgename angezeigt? |
q - Programm beenden |
- |
Programm beenden |
Hardcopy