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.

460 lines
12 KiB

  1. # Yhteystiedot ja osoitekirja 1/3
  2. Tämä harjoitus pohjautuu seuraavaan kahteen luokkaan sekä erikseen toteutettavaan `AddressBookApp`-luokkaan.
  3. **Contact-luokka:**
  4. ```
  5. // tiedosto Contact.java
  6. public class Contact {
  7. private String name;
  8. private String email;
  9. private String phone;
  10. public Contact(String name, String email, String phone) {
  11. this.name = name;
  12. this.email = email;
  13. this.phone = phone;
  14. }
  15. public String getName() {
  16. return this.name;
  17. }
  18. public String toString() {
  19. // Haluttu muoto: "Maija Meikäläinen (email: foo@bar.fi, phone: 5555)"
  20. return this.name + " (email: " + this.email + ", phone: " + this.phone + ")";
  21. }
  22. }
  23. ```
  24. **AddressBook-luokka:**
  25. ```
  26. // tiedosto AddressBook.java
  27. import java.util.ArrayList;
  28. import java.util.Comparator;
  29. import java.util.List;
  30. import java.util.stream.Collectors;
  31. public class AddressBook {
  32. private List<Contact> contacts;
  33. public AddressBook() {
  34. this.contacts = new ArrayList<>();
  35. }
  36. public boolean add(Contact newContact) {
  37. this.contacts.add(newContact);
  38. return true;
  39. }
  40. public Contact search(String keyword) {
  41. for (Contact current : this.contacts) {
  42. String name = current.getName();
  43. if (name != null && name.toLowerCase().contains(keyword.toLowerCase())) {
  44. return current; // palautetaan löytynyt arvo heti
  45. }
  46. }
  47. return null; // palautetaan null, jos ei löytynyt
  48. }
  49. @Override
  50. public String toString() {
  51. // TODO: toteuta tämä metodi. Metodin tulee muodostaa merkkijono,
  52. // joka sisältää kaikki yhteystiedot omilla riveillään.
  53. return "TODO";
  54. }
  55. }
  56. ```
  57. ## Ohjelman komentorivikäyttöliittymä
  58. Tehtävänä on tällä kertaa rakentaa uusi ohjelmaluokka `AddressBookApp`, joka mahdollistaa yhteystietojen käsittelyn `AddressBook` ja `Contact`-olioiden kanssa seuraavilla komennoilla:
  59. ```
  60. This is an address book application. Available commands:
  61. list
  62. help
  63. add <name>, <email>, <phone>
  64. search <name>
  65. exit
  66. ```
  67. 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.
  68. ## Syötteiden pyytäminen
  69. Tämän sovelluksen käyttöliittymässä syötettä pyydettäessä ruudulle tulostetaan `>` -merkki syötteen odottamisen merkiksi:
  70. ```
  71. System.out.print("> ");
  72. ```
  73. 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:
  74. ```
  75. > list
  76. ```
  77. ja
  78. ```
  79. > exit
  80. ```
  81. Moniosaisia komentoja ovat puolestaan esimerkiksi:
  82. ```
  83. > add Maija Meikäläinen, maija@example.com, +35850555556
  84. ```
  85. ```
  86. > search Matti
  87. ```
  88. ## Syötteiden lukeminen Scannerilla
  89. Syötteet kannattaa tässä sovelluksessa lukea kahdessa osassa. Ensin luetaan syötteen ensimmäinen sana ja sen jälkeen koko loppurivi:
  90. ```
  91. boolean running = true;
  92. while (running) {
  93. System.out.print("> ");
  94. String command = input.next();
  95. String theRest = input.nextLine().trim();
  96. // ... toimintalogiikka
  97. }
  98. ```
  99. 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.
  100. Jos käyttäjän syöte on `add Maija Meikäläinen, maija@example.com, +35850555556`, tulee muuttujien arvoiksi seuraavat:
  101. | command | theRest |
  102. |---------|----------------------------------------------------|
  103. | add | Maija Meikäläinen, maija@example.com, +35850555556 |
  104. `theRest`-muuttuja siis sisältää rivin koko loppuosan, joka `add`-komennon tapauksessa kannattaa pilkkoa String-luokan `split`-metodilla pilkkujen kohdalta:
  105. ```
  106. String[] parts = theRest.split(",");
  107. String name = parts[0].trim();
  108. String email = parts[1].trim();
  109. String phone = parts[2].trim();
  110. ```
  111. ## Ohjelman logiikan haarauttaminen
  112. Syötteen lukemisen jälkeen voidaan koodi haarauttaa joko Javan perusteista tutulla `if-else if-else`-ketjulla tai `switch case`-rakenteella:
  113. ```
  114. switch (command) {
  115. case "help":
  116. // tulosta ohje
  117. break;
  118. case "list":
  119. // tulosta osoitekirjan koko sisältö
  120. break;
  121. case "add":
  122. // käytä annettua nimeä, emailia ja puhelinnumeroa luodaksesi uuden yhteystiedon
  123. break;
  124. case "search":
  125. // etsi yhteystietoa ja tulosta se
  126. break;
  127. case "exit":
  128. // poistu ohjelmasta
  129. break;
  130. default:
  131. // tunnistamaton komento:
  132. System.out.println("Unknown command");
  133. break;
  134. }
  135. ```
  136. `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.
  137. ## Käyttöliittymän ominaisuudet
  138. Sinun ei tarvitse toteuttaa kaikkia komentoja ohjelman ensimmäiseen versioon, vaan **toteuta ominaisuudet yksittäin sitä mukaan kun tarvitset niitä.**
  139. ### ADD
  140. add-komennon tulee lisätä yhdelle riville lisätyt yhteystiedot uutena oliona `AddressBook`-osoitekirjaan:
  141. ```
  142. > add Maija Meikäläinen, maija@example.com, +35850555556
  143. Added Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
  144. ```
  145. ### SEARCH
  146. Jos annettu hakusana löytyy jostain osoitelistan yhteystiedosta, ohjelman tulee tulostaa kyseinen yhteystieto. Jos yhteystietoa ei löydy, tulostetaan siitä kertova viesti:
  147. ```
  148. > search Maija
  149. Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
  150. > search Ville
  151. Ville does not match any contact.
  152. ```
  153. ### LIST
  154. Komennon `list` jälkeen ohjelman tulee tulostaa kukin yhteystieto omalla rivillään:
  155. ```
  156. > list
  157. Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
  158. Matti Meikäläinen (email: matti@example.com, phone: +35850555555)
  159. ```
  160. ### HELP
  161. Käyttäjän syötettyä komennon `help` ohjelman tulee tulostaa sama ohjeteksti kuin ohjelman käynnistyksessä:
  162. ```
  163. This is an address book application. Available commands:
  164. list
  165. help
  166. add <name>, <email>, <phone>
  167. search <name>
  168. exit
  169. ```
  170. ### EXIT
  171. Käyttäjän kirjoittaessa syötteeksi "exit" ohjelman tulee tulostaa teksti "Bye!" ja lopettaa suorituksensa:
  172. ```
  173. > exit
  174. Bye!
  175. ```
  176. ### Esimerkki
  177. ```
  178. This is an address book application. Available commands:
  179. list
  180. help
  181. add <name>, <email>, <phone>
  182. search <name>
  183. exit
  184. > add Maija Meikäläinen, maija@example.com, +35850555556
  185. Added Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
  186. > add Matti Meikäläinen, matti@example.com, +35850555555
  187. Added Matti Meikäläinen (email: matti@example.com, phone: +35850555555)
  188. > search Matti
  189. Matti Meikäläinen (email: matti@example.com, phone: +35850555555)
  190. > search Maija
  191. Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
  192. > list
  193. Maija Meikäläinen (email: maija@example.com, phone: +35850555556)
  194. Matti Meikäläinen (email: matti@example.com, phone: +35850555555)
  195. > search Ville
  196. Ville does not match any contact.
  197. > exit
  198. Bye!
  199. ```
  200. **AddressBookApp.java
  201. ```
  202. import java.util.ArrayList;
  203. import java.util.List;
  204. import java.util.Scanner;
  205. public class AddressBookApp {
  206. private static String welcomeText = "This is an address book application. Available commands:\n";
  207. private static String commandMenu = " list\n" +
  208. " help\n" +
  209. " add <name>, <email>, <phone>\n" +
  210. " search <name>\n" +
  211. " exit\n";
  212. // Keep here instead of translating to a local variable
  213. private static String inputCLIPrefix = "> ";
  214. // Keep added addressBook values during application runtime
  215. private static AddressBook addressBook = new AddressBook();
  216. public static void main(String[] args) {
  217. System.out.print(welcomeText + commandMenu);
  218. while (true) {
  219. runApp();
  220. }
  221. }
  222. private static void runApp() {
  223. Scanner inputPrompt = new Scanner(System.in);
  224. Object[] getInput = parseInput(inputCLIPrefix, inputPrompt);
  225. String inputCmd = String.valueOf(getInput[0]);
  226. // TODO Avoid risk of ClassCastException
  227. List<String> inputArgs = (ArrayList<String>)getInput[1];
  228. int argsCount = 3;
  229. switch(inputCmd) {
  230. case "list":
  231. System.out.println(addressBook.toString());
  232. break;
  233. case "help":
  234. System.out.print(welcomeText + commandMenu);
  235. break;
  236. case "add":
  237. // TODO Should this be checked in AddressBook.add method instead?
  238. if (inputArgs.size() != argsCount) {
  239. System.err.printf("Invalid count of input arguments (expected %d).\n", argsCount);
  240. break;
  241. }
  242. String name = String.valueOf(inputArgs.get(0));
  243. String email = String.valueOf(inputArgs.get(1));
  244. String phone = String.valueOf(inputArgs.get(2));
  245. Contact contact = new Contact(name, email, phone);
  246. // NOTE: Else condition not needed as 3 arguments is always passed
  247. // to this object method call
  248. if(addressBook.add(contact)) {
  249. System.out.printf("Added %s\n", contact.toString());
  250. }
  251. break;
  252. case "search":
  253. String searchTerm = String.valueOf(inputArgs.get(0));
  254. Contact match = addressBook.search(searchTerm);
  255. if (match == null) {
  256. System.out.printf("%s does not match any contact.\n", searchTerm);
  257. } else {
  258. System.out.println(match.toString());
  259. }
  260. break;
  261. case "exit":
  262. System.out.print("Bye!\n");
  263. System.exit(0);
  264. default:
  265. System.err.println("Command not found.");
  266. //break;
  267. }
  268. }
  269. private static Object[] parseInput(String prefix, Scanner inputRaw) {
  270. System.out.print(prefix);
  271. String inputR = inputRaw.nextLine();
  272. String command = inputR.split(" ")[0];
  273. String[] theRest = inputR.split(",");
  274. theRest[0] = theRest[0].replaceAll("^\\s*" + command + "\\s*","");
  275. List<String> arguments = new ArrayList<String>();
  276. for (int i = 0; i < theRest.length; i++) {
  277. arguments.add(theRest[i].trim());
  278. }
  279. Object[] parsedOutput = new Object[2];
  280. parsedOutput[0] = command;
  281. parsedOutput[1] = arguments;
  282. return parsedOutput;
  283. }
  284. }
  285. ```
  286. **AddressBook.java**
  287. ```
  288. import java.util.ArrayList;
  289. import java.util.List;
  290. public class AddressBook {
  291. private List<Contact> contacts;
  292. public AddressBook() {
  293. this.contacts = new ArrayList<>();
  294. }
  295. public boolean add(Contact newContact) {
  296. this.contacts.add(newContact);
  297. return true;
  298. }
  299. public Contact search(String keyword) {
  300. for (Contact current : this.contacts) {
  301. String name = current.getName();
  302. if (name != null && name.toLowerCase().contains(keyword.toLowerCase())) {
  303. return current; // palautetaan löytynyt arvo heti
  304. }
  305. }
  306. return null; // palautetaan null, jos ei löytynyt
  307. }
  308. @Override
  309. public String toString() {
  310. String returnString = "";
  311. for (Contact contact : this.contacts) {
  312. returnString += contact + "\n";
  313. }
  314. return returnString;
  315. }
  316. }
  317. ```
  318. **Contact.java**
  319. ```
  320. public class Contact {
  321. private String name;
  322. private String email;
  323. private String phone;
  324. // NOTE: This does not validate or check input String values
  325. // (Is name actually a name, email a valid email, and phone number an actual phone number?)
  326. public Contact(String name, String email, String phone) {
  327. this.name = name;
  328. this.email = email;
  329. this.phone = phone;
  330. }
  331. public String getName() {
  332. return this.name;
  333. }
  334. public String toString() {
  335. // Haluttu muoto: "Maija Meikäläinen (email: foo@bar.fi, phone: 5555)"
  336. return this.name + " (email: " + this.email + ", phone: " + this.phone + ")";
  337. }
  338. }
  339. ```