Java fundamentals through coding exercises
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

12 KiB

Yhteystiedot ja osoitekirja 1/3

Tämä harjoitus pohjautuu seuraavaan kahteen luokkaan sekä erikseen toteutettavaan AddressBookApp-luokkaan.

Contact-luokka:

// tiedosto Contact.java
public class Contact {
private String name;
private String email;
private String phone;
public Contact(String name, String email, String phone) {
this.name = name;
this.email = email;
this.phone = phone;
}
public String getName() {
return this.name;
}
public String toString() {
// Haluttu muoto: "Maija Meikäläinen (email: foo@bar.fi, phone: 5555)"
return this.name + " (email: " + this.email + ", phone: " + this.phone + ")";
}
}

AddressBook-luokka:

// tiedosto AddressBook.java
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class AddressBook {
private List<Contact> contacts;
public AddressBook() {
this.contacts = new ArrayList<>();
}
public boolean add(Contact newContact) {
this.contacts.add(newContact);
return true;
}
public Contact search(String keyword) {
for (Contact current : this.contacts) {
String name = current.getName();
if (name != null && name.toLowerCase().contains(keyword.toLowerCase())) {
return current; // palautetaan löytynyt arvo heti
}
}
return null; // palautetaan null, jos ei löytynyt
}
@Override
public String toString() {
// TODO: toteuta tämä metodi. Metodin tulee muodostaa merkkijono, 
// joka sisältää kaikki yhteystiedot omilla riveillään.
return "TODO";
}
}

Ohjelman komentorivikäyttöliittymä

Tehtävänä on tällä kertaa rakentaa uusi ohjelmaluokka AddressBookApp, joka mahdollistaa yhteystietojen käsittelyn AddressBook ja Contact-olioiden kanssa seuraavilla komennoilla:

This is an address book application. Available commands:
list
help
add <name>, <email>, <phone>
search <name>
exit

Ohjelma tulee jakaa osiin siten, että vain AddressBookApp-luokka pyytää käyttäjältä syötteitä ja tulostaa tekstiä. Contact ja AddressBook ovat vain datan varastointia ja käsittelyä varten.

Syötteiden pyytäminen

Tämän sovelluksen käyttöliittymässä syötettä pyydettäessä ruudulle tulostetaan > -merkki syötteen odottamisen merkiksi:

System.out.print("> ");

Koska >-merkki tulostettiin print eikä println-metodilla, syöttää käyttäjä komentonsa samalle riville. Käyttäjän komento saattaa koostua yhdestä tai useasta osasta. Yhden sanan komentoja ovat esimerkiksi:

> list

ja

> exit

Moniosaisia komentoja ovat puolestaan esimerkiksi:

> add Maija Meikäläinen, maija@example.com, +35850555556
> search Matti

Syötteiden lukeminen Scannerilla

Syötteet kannattaa tässä sovelluksessa lukea kahdessa osassa. Ensin luetaan syötteen ensimmäinen sana ja sen jälkeen koko loppurivi:

boolean running = true;
while (running) {
System.out.print("> ");
String command = input.next();
String theRest = input.nextLine().trim();
// ... toimintalogiikka
}

Rivin ensimmäinen sana on aina komento (command), jonka perusteella valitaan mikä operaatio osoitekirjalle suoritetaan. Mahdollisesti annettua loppuriviä käytetään add- ja search-komentojen tapauksessa uuden yhteystiedon tietoina tai hakusanana.

Jos käyttäjän syöte on add Maija Meikäläinen, maija@example.com, +35850555556, tulee muuttujien arvoiksi seuraavat:

command theRest
add Maija Meikäläinen, maija@example.com, +35850555556

theRest-muuttuja siis sisältää rivin koko loppuosan, joka add-komennon tapauksessa kannattaa pilkkoa String-luokan split-metodilla pilkkujen kohdalta:

String[] parts = theRest.split(",");
String name = parts[0].trim();
String email = parts[1].trim();
String phone = parts[2].trim();

Ohjelman logiikan haarauttaminen

Syötteen lukemisen jälkeen voidaan koodi haarauttaa joko Javan perusteista tutulla if-else if-else-ketjulla tai switch case-rakenteella:

switch (command) {
case "help":
// tulosta ohje
break;
case "list":
// tulosta osoitekirjan koko sisältö
break;
case "add":
// käytä annettua nimeä, emailia ja puhelinnumeroa luodaksesi uuden yhteystiedon
break;
case "search":
// etsi yhteystietoa ja tulosta se
break;
case "exit":
// poistu ohjelmasta
break;
default:
// tunnistamaton komento:
System.out.println("Unknown command");
break;
}

Switch-case -rakenne ei ole tässä välttämätön, mutta se sopii tilanteeseen hyvin ja sen opettelu voi olla hyödyksi sinulle myös myöhemmin. Koska ohjelman halutaan toimivan toistaiseksi ja kysyvän käyttäjän syötettä aina uudelleen, on edellä esitetyt syötteen kyselyt ja ehtorakenteet toteutettava jonkin toistorakenteen sisään.

Käyttöliittymän ominaisuudet

Sinun ei tarvitse toteuttaa kaikkia komentoja ohjelman ensimmäiseen versioon, vaan toteuta ominaisuudet yksittäin sitä mukaan kun tarvitset niitä.

ADD

add-komennon tulee lisätä yhdelle riville lisätyt yhteystiedot uutena oliona AddressBook-osoitekirjaan:

> add Maija Meikäläinen, maija@example.com, +35850555556
Added Maija Meikäläinen (email: maija@example.com, phone: +35850555556)

Jos annettu hakusana löytyy jostain osoitelistan yhteystiedosta, ohjelman tulee tulostaa kyseinen yhteystieto. Jos yhteystietoa ei löydy, tulostetaan siitä kertova viesti:

> search Maija
Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
> search Ville
Ville does not match any contact.

LIST

Komennon list jälkeen ohjelman tulee tulostaa kukin yhteystieto omalla rivillään:

> list
Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
Matti Meikäläinen (email: matti@example.com, phone: +35850555555)

HELP

Käyttäjän syötettyä komennon help ohjelman tulee tulostaa sama ohjeteksti kuin ohjelman käynnistyksessä:

This is an address book application. Available commands:
list
help
add <name>, <email>, <phone>
search <name>
exit

EXIT

Käyttäjän kirjoittaessa syötteeksi "exit" ohjelman tulee tulostaa teksti "Bye!" ja lopettaa suorituksensa:

> exit
Bye!

Esimerkki

This is an address book application. Available commands:
list
help
add <name>, <email>, <phone>
search <name>
exit
> add Maija Meikäläinen, maija@example.com, +35850555556
Added Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
> add Matti Meikäläinen, matti@example.com, +35850555555
Added Matti Meikäläinen (email: matti@example.com, phone: +35850555555)
> search Matti
Matti Meikäläinen (email: matti@example.com, phone: +35850555555)
> search Maija
Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
> list
Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
Matti Meikäläinen (email: matti@example.com, phone: +35850555555)
> search Ville
Ville does not match any contact.
> exit
Bye!

**AddressBookApp.java

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class AddressBookApp {
private static String welcomeText = "This is an address book application. Available commands:\n";
private static String commandMenu = " list\n" +
" help\n" +
" add <name>, <email>, <phone>\n" +
" search <name>\n" +
" exit\n";
// Keep here instead of translating to a local variable
private static String inputCLIPrefix  = "> ";
// Keep added addressBook values during application runtime
private static AddressBook addressBook = new AddressBook();
public static void main(String[] args) {
System.out.print(welcomeText + commandMenu);
while (true) {
runApp();
}
}
private static void runApp() {
Scanner inputPrompt    = new Scanner(System.in);
Object[] getInput      = parseInput(inputCLIPrefix, inputPrompt);
String inputCmd        = String.valueOf(getInput[0]);
// TODO Avoid risk of ClassCastException
List<String> inputArgs = (ArrayList<String>)getInput[1];
int argsCount          = 3;
switch(inputCmd) {
case "list":
System.out.println(addressBook.toString());
break;
case "help":
System.out.print(welcomeText + commandMenu);
break;
case "add":
// TODO Should this be checked in AddressBook.add method instead?
if (inputArgs.size() != argsCount) {
System.err.printf("Invalid count of input arguments (expected %d).\n", argsCount);
break;
}
String name  = String.valueOf(inputArgs.get(0));
String email = String.valueOf(inputArgs.get(1));
String phone = String.valueOf(inputArgs.get(2));
Contact contact = new Contact(name, email, phone);
// NOTE: Else condition not needed as 3 arguments is always passed
// to this object method call
if(addressBook.add(contact)) {
System.out.printf("Added %s\n", contact.toString());
}
break;
case "search":
String searchTerm  = String.valueOf(inputArgs.get(0));
Contact match      = addressBook.search(searchTerm);
if (match == null) {
System.out.printf("%s does not match any contact.\n", searchTerm);
} else {
System.out.println(match.toString());
}
break;
case "exit":
System.out.print("Bye!\n");
System.exit(0);
default:
System.err.println("Command not found.");
//break;
}
}
private static Object[] parseInput(String prefix, Scanner inputRaw) {
System.out.print(prefix);
String inputR = inputRaw.nextLine();
String command   = inputR.split(" ")[0];
String[] theRest = inputR.split(",");
theRest[0] = theRest[0].replaceAll("^\\s*" + command + "\\s*","");
List<String> arguments = new ArrayList<String>();
for (int i = 0; i < theRest.length; i++) {
arguments.add(theRest[i].trim());
}
Object[] parsedOutput = new Object[2];
parsedOutput[0] = command;
parsedOutput[1] = arguments;
return parsedOutput;
}
}

AddressBook.java

import java.util.ArrayList;
import java.util.List;
public class AddressBook {
private List<Contact> contacts;
public AddressBook() {
this.contacts = new ArrayList<>();
}
public boolean add(Contact newContact) {
this.contacts.add(newContact);
return true;
}
public Contact search(String keyword) {
for (Contact current : this.contacts) {
String name = current.getName();
if (name != null && name.toLowerCase().contains(keyword.toLowerCase())) {
return current; // palautetaan löytynyt arvo heti
}
}
return null; // palautetaan null, jos ei löytynyt
}
@Override
public String toString() {
String returnString = "";
for (Contact contact : this.contacts) {
returnString += contact + "\n";
}
return returnString;
}
}

Contact.java

public class Contact {
private String name;
private String email;
private String phone;
// NOTE: This does not validate or check input String values
// (Is name actually a name, email a valid email, and phone number an actual phone number?)
public Contact(String name, String email, String phone) {
this.name = name;
this.email = email;
this.phone = phone;
}
public String getName() {
return this.name;
}
public String toString() {
// Haluttu muoto: "Maija Meikäläinen (email: foo@bar.fi, phone: 5555)"
return this.name + " (email: " + this.email + ", phone: " + this.phone + ")";
}
}