diff --git a/2022-02/spring-00/architecture-origins/.gitignore b/2022-02/spring-00/architecture-origins/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/pom.xml b/2022-02/spring-00/architecture-origins/pom.xml new file mode 100644 index 00000000..0890067c --- /dev/null +++ b/2022-02/spring-00/architecture-origins/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + ru.otus + architecture-origins + 1.0 + + pom + + + solution01 + solution02_codeface + solution03_kiss + solution04_kiss + solution05_dry + solution06_dry + solution07_srp + solution08_srp + solution09_dip + solution10_ocp_isp + + diff --git a/2022-02/spring-00/architecture-origins/solution01/.gitignore b/2022-02/spring-00/architecture-origins/solution01/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution01/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution01/pom.xml b/2022-02/spring-00/architecture-origins/solution01/pom.xml new file mode 100644 index 00000000..c76439cc --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution01/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + solution01 + 1.0 + + + 11 + 11 + UTF-8 + + + + + diff --git a/2022-02/spring-00/architecture-origins/solution01/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution01/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..45b177a2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution01/src/main/java/ru/otus/Main.java @@ -0,0 +1,65 @@ +package ru.otus; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.stream.IntStream; + +public class Main { + + public static void main(String[] args) { + List strings = new ArrayList<>(); + Scanner sc = new Scanner(System.in); + + while (true) { + System.out.println("Выберите одно из следующих действий..."); + System.out.println("1. Вывести все заметки"); + System.out.println("2. Добавить заметку"); + System.out.println("3. Изменить заметку"); + System.out.println("4. Удалить заметку"); + System.out.println("5. Выйти"); + + try { + int i = Integer.parseInt(sc.nextLine()); + + if (i == 1) { + System.out.println("Заметки:"); + IntStream.range(1, strings.size() + 1) + .mapToObj(k -> k + " | " + strings.get(k - 1)) + .forEach(System.out::println); + System.out.println(); + } else if (i == 2) { + System.out.println("Введите текст заметки..."); + String s = sc.nextLine(); + strings.add(LocalDateTime.now() + " | " + s); + } else if (i == 3) { + System.out.println("Введите номер изменяемой заметки..."); + int k = Integer.parseInt(sc.nextLine()); + ; + if (k > 0 && k <= strings.size()) { + System.out.println("Введите текст заметки..."); + String s = sc.nextLine(); + strings.set(k - 1, LocalDateTime.now() + " | " + s); + } else { + System.out.println("Введен несуществующий номер заметки"); + } + } else if (i == 4) { + System.out.println("Введите номер удаляемой заметки..."); + int k = Integer.parseInt(sc.nextLine()); + if (k > 0 && k <= strings.size()) { + strings.remove(k - 1); + } else { + System.out.println("Введен несуществующий номер заметки"); + } + } else if (i == 5) { + break; + } else { + System.out.println("Введен неверный номер опции"); + } + } catch (NumberFormatException e) { + System.out.println("Ошибка при вводе числа"); + } + } + } +} diff --git a/2022-02/spring-00/architecture-origins/solution02_codeface/.gitignore b/2022-02/spring-00/architecture-origins/solution02_codeface/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution02_codeface/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution02_codeface/pom.xml b/2022-02/spring-00/architecture-origins/solution02_codeface/pom.xml new file mode 100644 index 00000000..85e56d77 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution02_codeface/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + solution02_codeface + 1.0 + + + 11 + 11 + UTF-8 + + + + + diff --git a/2022-02/spring-00/architecture-origins/solution02_codeface/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution02_codeface/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..ea54bae3 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution02_codeface/src/main/java/ru/otus/Main.java @@ -0,0 +1,79 @@ +package ru.otus; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.stream.IntStream; + +// Имена переменных, константы, выпрямление кода +public class Main { + + private static final int MENU_OPTION_SHOW_ALL_NOTES = 1; + private static final int MENU_OPTION_ADD_NEW_NOTE = 2; + private static final int MENU_OPTION_UPDATE_NOTE = 3; + private static final int MENU_OPTION_DELETE_NOTE = 4; + private static final int MENU_OPTION_EXIT = 5; + + public static void main(String[] args) { + var notes = new ArrayList(); + var userInput = new Scanner(System.in); + + while (true) { + System.out.println("Выберите одно из следующих действий..."); + System.out.println("1. Вывести все заметки"); + System.out.println("2. Добавить заметку"); + System.out.println("3. Изменить заметку"); + System.out.println("4. Удалить заметку"); + System.out.println("5. Выйти"); + + try { + var selectedOptionNumber = Integer.parseInt(userInput.nextLine()); + + if (selectedOptionNumber == MENU_OPTION_SHOW_ALL_NOTES) { + System.out.println("Заметки:"); + IntStream.range(1, notes.size() + 1) + .mapToObj(k -> k + " | " + notes.get(k - 1)) + .forEach(System.out::println); + System.out.println(); + } else if (selectedOptionNumber == MENU_OPTION_ADD_NEW_NOTE) { + System.out.println("Введите текст заметки..."); + var noteText = userInput.nextLine(); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.add(finalNoteText); + } else if (selectedOptionNumber == MENU_OPTION_UPDATE_NOTE) { + System.out.println("Введите номер изменяемой заметки..."); + + var updatedNoteNumber = Integer.parseInt(userInput.nextLine()); + + if (updatedNoteNumber <= 0 || updatedNoteNumber > notes.size()) { + System.out.println("Введен несуществующий номер заметки"); + continue; + } + + System.out.println("Введите текст заметки..."); + var noteText = userInput.nextLine(); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.set(updatedNoteNumber - 1, finalNoteText); + } else if (selectedOptionNumber == MENU_OPTION_DELETE_NOTE) { + System.out.println("Введите номер удаляемой заметки..."); + + var deletedNoteNumber = Integer.parseInt(userInput.nextLine()); + if (deletedNoteNumber <= 0 || deletedNoteNumber > notes.size()) { + System.out.println("Введен несуществующий номер заметки"); + continue; + } + + notes.remove(deletedNoteNumber - 1); + } else if (selectedOptionNumber == MENU_OPTION_EXIT) { + break; + } else { + System.out.println("Введен неверный номер опции"); + } + } catch (NumberFormatException e) { + System.out.println("Ошибка при вводе числа"); + } + + } + } +} diff --git a/2022-02/spring-00/architecture-origins/solution03_kiss/.gitignore b/2022-02/spring-00/architecture-origins/solution03_kiss/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution03_kiss/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution03_kiss/pom.xml b/2022-02/spring-00/architecture-origins/solution03_kiss/pom.xml new file mode 100644 index 00000000..08d5b82d --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution03_kiss/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + solution03_kiss + 1.0 + + + 11 + 11 + UTF-8 + + + + + diff --git a/2022-02/spring-00/architecture-origins/solution03_kiss/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution03_kiss/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..2364709c --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution03_kiss/src/main/java/ru/otus/Main.java @@ -0,0 +1,102 @@ +package ru.otus; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.stream.IntStream; + +// Вынос печати меню, чтения пункта меню и выполнения команд в методы +public class Main { + + private static final int MENU_OPTION_SHOW_ALL_NOTES = 1; + private static final int MENU_OPTION_ADD_NEW_NOTE = 2; + private static final int MENU_OPTION_UPDATE_NOTE = 3; + private static final int MENU_OPTION_DELETE_NOTE = 4; + private static final int MENU_OPTION_EXIT = 5; + + public static void main(String[] args) { + var notes = new ArrayList(); + var userInput = new Scanner(System.in); + + while (true) { + outputMenu(); + try { + var selectedMenuItem = readSelectedOptionNumberFrom(userInput); + + if (selectedMenuItem == MENU_OPTION_SHOW_ALL_NOTES) { + showAllNotes(notes); + } else if (selectedMenuItem == MENU_OPTION_ADD_NEW_NOTE) { + addNewNote(userInput, notes); + } else if (selectedMenuItem == MENU_OPTION_UPDATE_NOTE) { + updateNote(userInput, notes); + } else if (selectedMenuItem == MENU_OPTION_DELETE_NOTE) { + deleteNote(userInput, notes); + } else if (selectedMenuItem == MENU_OPTION_EXIT){ + break; + } else { + System.out.println("Введен неверный номер опции"); + } + } catch (NumberFormatException e) { + System.out.println("Ошибка при вводе числа"); + } + + } + } + + private static void outputMenu(){ + System.out.println("Выберите одно из следующих действий..."); + System.out.println("1. Вывести все заметки"); + System.out.println("2. Добавить заметку"); + System.out.println("3. Изменить заметку"); + System.out.println("4. Удалить заметку"); + System.out.println("5. Выйти"); + } + + private static int readSelectedOptionNumberFrom(Scanner userInput) { + return Integer.parseInt(userInput.nextLine()); + } + + private static void showAllNotes(List notes) { + System.out.println("Заметки:"); + IntStream.range(1, notes.size() + 1) + .mapToObj(k -> k + " | " + notes.get(k - 1)) + .forEach(System.out::println); + System.out.println(); + } + + private static void addNewNote(Scanner userInput, List notes) { + System.out.println("Введите текст заметки..."); + var noteText = userInput.nextLine(); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.add(finalNoteText); + } + + private static void updateNote(Scanner userInput, List notes) { + System.out.println("Введите номер изменяемой заметки..."); + + var updatedNoteNumber = Integer.parseInt(userInput.nextLine());; + if (updatedNoteNumber <= 0 || updatedNoteNumber > notes.size()) { + System.out.println("Введен несуществующий номер заметки"); + return; + } + + System.out.println("Введите текст заметки..."); + var noteText = userInput.nextLine(); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.set(updatedNoteNumber - 1, finalNoteText); + } + + private static void deleteNote(Scanner userInput, List notes) { + System.out.println("Введите номер удаляемой заметки..."); + + var deletedNoteNumber = Integer.parseInt(userInput.nextLine()); + if (deletedNoteNumber <= 0 || deletedNoteNumber > notes.size()) { + System.out.println("Введен несуществующий номер заметки"); + return; + } + + notes.remove(deletedNoteNumber - 1); + } + +} diff --git a/2022-02/spring-00/architecture-origins/solution04_kiss/.gitignore b/2022-02/spring-00/architecture-origins/solution04_kiss/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution04_kiss/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution04_kiss/pom.xml b/2022-02/spring-00/architecture-origins/solution04_kiss/pom.xml new file mode 100644 index 00000000..7f365752 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution04_kiss/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + solution04_kiss + 1.0 + + + 11 + 11 + UTF-8 + + + + + diff --git a/2022-02/spring-00/architecture-origins/solution04_kiss/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution04_kiss/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..9420d5a4 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution04_kiss/src/main/java/ru/otus/Main.java @@ -0,0 +1,107 @@ +package ru.otus; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.IntStream; + +// Уменьшение вложенности в main за счет выноса обработки команд в метод + executionFlag +public class Main { + + private static final int MENU_OPTION_SHOW_ALL_NOTES = 1; + private static final int MENU_OPTION_ADD_NEW_NOTE = 2; + private static final int MENU_OPTION_UPDATE_NOTE = 3; + private static final int MENU_OPTION_DELETE_NOTE = 4; + private static final int MENU_OPTION_EXIT = 5; + + public static void main(String[] args) { + var notes = new ArrayList(); + var userInput = new Scanner(System.in); + + var executionFlag = new AtomicBoolean(true); + while (executionFlag.get()) { + outputMenu(); + try { + var selectedMenuItem = readSelectedOptionNumberFrom(userInput); + processMenuCommand(selectedMenuItem, executionFlag, userInput, notes); + } catch (NumberFormatException e) { + System.out.println("Ошибка при вводе числа"); + } + } + } + + private static void outputMenu(){ + System.out.println("Выберите одно из следующих действий..."); + System.out.println("1. Вывести все заметки"); + System.out.println("2. Добавить заметку"); + System.out.println("3. Изменить заметку"); + System.out.println("4. Удалить заметку"); + System.out.println("5. Выйти"); + } + + private static void processMenuCommand(int selectedMenuItem, AtomicBoolean executionFlag, + Scanner userInput, List notes) { + if (selectedMenuItem == MENU_OPTION_SHOW_ALL_NOTES) { + showAllNotes(notes); + } else if (selectedMenuItem == MENU_OPTION_ADD_NEW_NOTE) { + addNewNote(userInput, notes); + } else if (selectedMenuItem == MENU_OPTION_UPDATE_NOTE) { + updateNote(userInput, notes); + } else if (selectedMenuItem == MENU_OPTION_DELETE_NOTE) { + deleteNote(userInput, notes); + } else if (selectedMenuItem == MENU_OPTION_EXIT){ + executionFlag.set(false); + } else { + System.out.println("Введен неверный номер опции"); + } + } + + private static int readSelectedOptionNumberFrom(Scanner userInput) { + return Integer.parseInt(userInput.nextLine()); + } + + private static void showAllNotes(List notes) { + System.out.println("Заметки:"); + IntStream.range(1, notes.size() + 1) + .mapToObj(k -> k + " | " + notes.get(k - 1)) + .forEach(System.out::println); + System.out.println(); + } + + private static void addNewNote(Scanner userInput, List notes) { + System.out.println("Введите текст заметки..."); + var noteText = userInput.nextLine(); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.add(finalNoteText); + } + + private static void updateNote(Scanner userInput, List notes) { + System.out.println("Введите номер изменяемой заметки..."); + + var updatedNoteNumber = Integer.parseInt(userInput.nextLine());; + if (updatedNoteNumber <= 0 || updatedNoteNumber > notes.size()) { + System.out.println("Введен несуществующий номер заметки"); + return; + } + + System.out.println("Введите текст заметки..."); + var noteText = userInput.nextLine(); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.set(updatedNoteNumber - 1, finalNoteText); + } + + private static void deleteNote(Scanner userInput, List notes) { + System.out.println("Введите номер удаляемой заметки..."); + + var deletedNoteNumber = Integer.parseInt(userInput.nextLine()); + if (deletedNoteNumber <= 0 || deletedNoteNumber > notes.size()) { + System.out.println("Введен несуществующий номер заметки"); + return; + } + + notes.remove(deletedNoteNumber - 1); + } + +} diff --git a/2022-02/spring-00/architecture-origins/solution05_dry/.gitignore b/2022-02/spring-00/architecture-origins/solution05_dry/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution05_dry/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution05_dry/pom.xml b/2022-02/spring-00/architecture-origins/solution05_dry/pom.xml new file mode 100644 index 00000000..1eaafeec --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution05_dry/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + solution05_dry + 1.0 + + + 11 + 11 + UTF-8 + + + + + diff --git a/2022-02/spring-00/architecture-origins/solution05_dry/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution05_dry/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..bf81db2c --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution05_dry/src/main/java/ru/otus/Main.java @@ -0,0 +1,118 @@ +package ru.otus; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.IntStream; + +// Вынос логики ввода-вывода +public class Main { + + private static final int MENU_OPTION_SHOW_ALL_NOTES = 1; + private static final int MENU_OPTION_ADD_NEW_NOTE = 2; + private static final int MENU_OPTION_UPDATE_NOTE = 3; + private static final int MENU_OPTION_DELETE_NOTE = 4; + private static final int MENU_OPTION_EXIT = 5; + + public static void main(String[] args) { + var notes = new ArrayList(); + var userInput = new Scanner(System.in); + + var executionFlag = new AtomicBoolean(true); + while (executionFlag.get()) { + outputMenu(); + try { + var selectedMenuItem = readSelectedOptionNumberFrom(userInput); + processMenuCommand(selectedMenuItem, executionFlag, userInput, notes); + } catch (NumberFormatException e) { + outputString("Ошибка при вводе числа"); + } + } + } + + private static void outputMenu(){ + outputString("Выберите одно из следующих действий..."); + outputString("1. Вывести все заметки"); + outputString("2. Добавить заметку"); + outputString("3. Изменить заметку"); + outputString("4. Удалить заметку"); + outputString("5. Выйти"); + } + + private static void processMenuCommand(int selectedMenuItem, AtomicBoolean executionFlag, + Scanner userInput, List notes) { + if (selectedMenuItem == MENU_OPTION_SHOW_ALL_NOTES) { + showAllNotes(notes); + } else if (selectedMenuItem == MENU_OPTION_ADD_NEW_NOTE) { + addNewNote(userInput, notes); + } else if (selectedMenuItem == MENU_OPTION_UPDATE_NOTE) { + updateNote(userInput, notes); + } else if (selectedMenuItem == MENU_OPTION_DELETE_NOTE) { + deleteNote(userInput, notes); + } else if (selectedMenuItem == MENU_OPTION_EXIT){ + executionFlag.set(false); + } else { + outputString("Введен неверный номер опции"); + } + } + + private static int readSelectedOptionNumberFrom(Scanner userInput) { + return readInt(userInput); + } + + private static void showAllNotes(List notes) { + outputString("Заметки:"); + IntStream.range(1, notes.size() + 1) + .mapToObj(k -> k + " | " + notes.get(k - 1)) + .forEach(Main::outputString); + outputString(""); + } + + private static void addNewNote(Scanner userInput, List notes) { + var noteText = readStringWithPrompt(userInput, "Введите текст заметки..."); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.add(finalNoteText); + } + + private static void updateNote(Scanner userInput, List notes) { + int updatedNoteNumber = readIntWithPrompt(userInput, "Введите номер изменяемой заметки..."); + if (updatedNoteNumber <= 0 || updatedNoteNumber > notes.size()) { + outputString("Введен несуществующий номер заметки"); + return; + } + var noteText = readStringWithPrompt(userInput, "Введите текст заметки..."); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.set(updatedNoteNumber - 1, finalNoteText); + } + + private static void deleteNote(Scanner userInput, List notes) { + var deletedNoteNumber = readIntWithPrompt(userInput, "Введите номер удаляемой заметки..."); + if (deletedNoteNumber <= 0 || deletedNoteNumber > notes.size()) { + outputString("Введен несуществующий номер заметки"); + return; + } + + notes.remove(deletedNoteNumber - 1); + } + + private static void outputString(String s){ + System.out.println(s); + } + + private static int readInt(Scanner userInput){ + return Integer.parseInt(userInput.nextLine()); + } + + private static int readIntWithPrompt(Scanner userInput, String prompt){ + outputString(prompt); + return Integer.parseInt(userInput.nextLine()); + } + + private static String readStringWithPrompt(Scanner userInput, String prompt){ + outputString(prompt); + return userInput.nextLine(); + } + +} diff --git a/2022-02/spring-00/architecture-origins/solution06_dry/.gitignore b/2022-02/spring-00/architecture-origins/solution06_dry/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution06_dry/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution06_dry/pom.xml b/2022-02/spring-00/architecture-origins/solution06_dry/pom.xml new file mode 100644 index 00000000..292cf7ec --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution06_dry/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + solution06_dry + 1.0 + + + 11 + 11 + UTF-8 + + + + + diff --git a/2022-02/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..d652ca12 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/Main.java @@ -0,0 +1,127 @@ +package ru.otus; + +import ru.otus.exceptions.MenuItemIndexOutOfBoundsException; +import ru.otus.exceptions.NoteIndexOutOfBoundsException; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.IntStream; + +// Вынос проверки существования выбранной команды в метод checkNoteNumber, +// централизованная обработка ошибок +public class Main { + + private static final int MENU_OPTION_SHOW_ALL_NOTES = 1; + private static final int MENU_OPTION_ADD_NEW_NOTE = 2; + private static final int MENU_OPTION_UPDATE_NOTE = 3; + private static final int MENU_OPTION_DELETE_NOTE = 4; + private static final int MENU_OPTION_EXIT = 5; + + public static void main(String[] args) { + var notes = new ArrayList(); + var userInput = new Scanner(System.in); + + var executionFlag = new AtomicBoolean(true); + while (executionFlag.get()) { + outputMenu(); + try { + var selectedMenuItem = readSelectedOptionNumberFrom(userInput); + processMenuCommand(selectedMenuItem, executionFlag, userInput, notes); + + } catch (NumberFormatException e) { + outputString("Ошибка при вводе числа"); + } catch (MenuItemIndexOutOfBoundsException e) { + outputString("Введен неверный номер опции"); + } catch (NoteIndexOutOfBoundsException e) { + outputString("Введен несуществующий номер заметки"); + } + } + } + + private static void outputMenu(){ + outputString("Выберите одно из следующих действий..."); + outputString("1. Вывести все заметки"); + outputString("2. Добавить заметку"); + outputString("3. Изменить заметку"); + outputString("4. Удалить заметку"); + outputString("5. Выйти"); + } + + private static void processMenuCommand(int selectedMenuItemIndex, AtomicBoolean executionFlag, + Scanner userInput, List notes) { + if (selectedMenuItemIndex == MENU_OPTION_SHOW_ALL_NOTES) { + showAllNotes(notes); + } else if (selectedMenuItemIndex == MENU_OPTION_ADD_NEW_NOTE) { + addNewNote(userInput, notes); + } else if (selectedMenuItemIndex == MENU_OPTION_UPDATE_NOTE) { + updateNote(userInput, notes); + } else if (selectedMenuItemIndex == MENU_OPTION_DELETE_NOTE) { + deleteNote(userInput, notes); + } else if (selectedMenuItemIndex == MENU_OPTION_EXIT){ + executionFlag.set(false); + } else { + throw new MenuItemIndexOutOfBoundsException("Given menu item index is out of range"); + } + } + + private static int readSelectedOptionNumberFrom(Scanner userInput) { + return readInt(userInput); + } + + private static void showAllNotes(List notes) { + outputString("Заметки:"); + IntStream.range(1, notes.size() + 1) + .mapToObj(k -> k + " | " + notes.get(k - 1)) + .forEach(Main::outputString); + outputString(""); + } + + private static void addNewNote(Scanner userInput, List notes) { + var noteText = readStringWithPrompt(userInput, "Введите текст заметки..."); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.add(finalNoteText); + } + + private static void checkNoteNumber(int noteNumber, List notes) { + if (noteNumber <= 0 || noteNumber > notes.size()) { + throw new NoteIndexOutOfBoundsException("Given number of note is out of range"); + } + } + + private static void updateNote(Scanner userInput, List notes) { + var updatedNoteNumber = readIntWithPrompt(userInput, "Введите номер изменяемой заметки..."); + checkNoteNumber(updatedNoteNumber, notes); + + var noteText = readStringWithPrompt(userInput, "Введите текст заметки..."); + var finalNoteText = LocalDateTime.now() + " | " + noteText; + notes.set(updatedNoteNumber - 1, finalNoteText); + } + + private static void deleteNote(Scanner userInput, List notes) { + var deletedNoteNumber = readIntWithPrompt(userInput, "Введите номер удаляемой заметки..."); + checkNoteNumber(deletedNoteNumber, notes); + + notes.remove(deletedNoteNumber - 1); + } + + private static void outputString(String s){ + System.out.println(s); + } + + private static int readInt(Scanner userInput){ + return Integer.parseInt(userInput.nextLine()); + } + + private static int readIntWithPrompt(Scanner userInput, String prompt){ + outputString(prompt); + return Integer.parseInt(userInput.nextLine()); + } + + private static String readStringWithPrompt(Scanner userInput, String prompt){ + outputString(prompt); + return userInput.nextLine(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java new file mode 100644 index 00000000..3a3124e9 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException { + public MenuItemIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java new file mode 100644 index 00000000..0747775d --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException { + public NoteIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution07_srp/.gitignore b/2022-02/spring-00/architecture-origins/solution07_srp/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution07_srp/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution07_srp/pom.xml b/2022-02/spring-00/architecture-origins/solution07_srp/pom.xml new file mode 100644 index 00000000..4f3ab886 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution07_srp/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + solution07_srp + 1.0 + + + 11 + 11 + UTF-8 + + + + + diff --git a/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..99416733 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/Main.java @@ -0,0 +1,109 @@ +package ru.otus; + +import ru.otus.exceptions.NoteIndexOutOfBoundsException; +import ru.otus.exceptions.MenuItemIndexOutOfBoundsException; +import ru.otus.model.Note; +import ru.otus.services.ConsoleIOService; +import ru.otus.services.NoteConverter; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.IntStream; + +// Note, ConsoleIOService, NoteConverter +public class Main { + + private static final int MENU_OPTION_SHOW_ALL_NOTES = 1; + private static final int MENU_OPTION_ADD_NEW_NOTE = 2; + private static final int MENU_OPTION_UPDATE_NOTE = 3; + private static final int MENU_OPTION_DELETE_NOTE = 4; + private static final int MENU_OPTION_EXIT = 5; + + public static void main(String[] args) { + var notes = new ArrayList(); + var ioService = new ConsoleIOService(); + + var executionFlag = new AtomicBoolean(true); + while (executionFlag.get()) { + outputMenu(ioService); + try { + var selectedMenuItem = readSelectedOptionNumberFrom(ioService); + processMenuCommand(selectedMenuItem, executionFlag, ioService, notes); + + } catch (NumberFormatException e) { + ioService.outputString("Ошибка при вводе числа"); + } catch (MenuItemIndexOutOfBoundsException e) { + ioService.outputString("Введен неверный номер опции"); + } catch (NoteIndexOutOfBoundsException e) { + ioService.outputString("Введен несуществующий номер заметки"); + } + } + } + + private static void outputMenu(ConsoleIOService ioService){ + ioService.outputString("Выберите одно из следующих действий..."); + ioService.outputString("1. Вывести все заметки"); + ioService.outputString("2. Добавить заметку"); + ioService.outputString("3. Изменить заметку"); + ioService.outputString("4. Удалить заметку"); + ioService.outputString("5. Выйти"); + } + + private static void processMenuCommand(int selectedMenuItemIndex, AtomicBoolean executionFlag, + ConsoleIOService ioService, List notes) { + if (selectedMenuItemIndex == MENU_OPTION_SHOW_ALL_NOTES) { + showAllNotes(ioService, notes); + } else if (selectedMenuItemIndex == MENU_OPTION_ADD_NEW_NOTE) { + addNewNote(ioService, notes); + } else if (selectedMenuItemIndex == MENU_OPTION_UPDATE_NOTE) { + updateNote(ioService, notes); + } else if (selectedMenuItemIndex == MENU_OPTION_DELETE_NOTE) { + deleteNote(ioService, notes); + } else if (selectedMenuItemIndex == MENU_OPTION_EXIT){ + executionFlag.set(false); + } else { + throw new MenuItemIndexOutOfBoundsException("Given menu item index is out of range"); + } + } + + private static int readSelectedOptionNumberFrom(ConsoleIOService ioService) { + return ioService.readInt(); + } + + private static void showAllNotes(ConsoleIOService ioService, List notes) { + var noteConverter = new NoteConverter(); + ioService.outputString("Заметки:"); + IntStream.range(1, notes.size() + 1) + .mapToObj(k -> noteConverter.convertNoteToString(k, notes.get(k - 1))) + .forEach(ioService::outputString); + ioService.outputString(""); + } + + private static void addNewNote(ConsoleIOService ioService, List notes) { + var noteText = ioService.readStringWithPrompt("Введите текст заметки..."); + notes.add( Note.of(noteText)); + } + + private static void checkNoteNumber(int noteNumber, List notes) { + if (noteNumber <= 0 || noteNumber > notes.size()) { + throw new NoteIndexOutOfBoundsException("Given number of note is out of range"); + } + } + + private static void updateNote(ConsoleIOService ioService, List notes) { + var updatedNoteNumber = ioService.readIntWithPrompt("Введите номер изменяемой заметки..."); + checkNoteNumber(updatedNoteNumber, notes); + + var noteText = ioService.readStringWithPrompt("Введите текст заметки..."); + notes.set(updatedNoteNumber - 1, Note.of(noteText)); + } + + private static void deleteNote(ConsoleIOService ioService, List notes) { + var deletedNoteNumber = ioService.readIntWithPrompt("Введите номер удаляемой заметки..."); + checkNoteNumber(deletedNoteNumber, notes); + + notes.remove(deletedNoteNumber - 1); + } + +} diff --git a/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java new file mode 100644 index 00000000..3a3124e9 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException { + public MenuItemIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java new file mode 100644 index 00000000..0747775d --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException { + public NoteIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/model/Note.java b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/model/Note.java new file mode 100644 index 00000000..85261641 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/model/Note.java @@ -0,0 +1,26 @@ +package ru.otus.model; + +import java.time.LocalDateTime; + +public class Note { + private final LocalDateTime creationTime; + private final String text; + + public Note(String text) { + this.creationTime = LocalDateTime.now(); + this.text = text; + } + + public static Note of(String text) { + return new Note(text); + } + + public LocalDateTime getCreationTime() { + return creationTime; + } + + public String getText() { + return text; + } + +} diff --git a/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/ConsoleIOService.java b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/ConsoleIOService.java new file mode 100644 index 00000000..06e52546 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/ConsoleIOService.java @@ -0,0 +1,29 @@ +package ru.otus.services; + +import java.util.Scanner; + +public class ConsoleIOService { + private final Scanner userInput; + + public ConsoleIOService() { + userInput = new Scanner(System.in); + } + + public void outputString(String s){ + System.out.println(s); + } + + public int readInt(){ + return Integer.parseInt(userInput.nextLine()); + } + + public int readIntWithPrompt(String prompt){ + outputString(prompt); + return Integer.parseInt(userInput.nextLine()); + } + + public String readStringWithPrompt(String prompt){ + outputString(prompt); + return userInput.nextLine(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/NoteConverter.java b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/NoteConverter.java new file mode 100644 index 00000000..6c5b57cd --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/NoteConverter.java @@ -0,0 +1,9 @@ +package ru.otus.services; + +import ru.otus.model.Note; + +public class NoteConverter { + public String convertNoteToString(int noteNumber, Note note) { + return noteNumber + " | " + note.getCreationTime() + " | " + note.getText(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/.gitignore b/2022-02/spring-00/architecture-origins/solution08_srp/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/pom.xml b/2022-02/spring-00/architecture-origins/solution08_srp/pom.xml new file mode 100644 index 00000000..f462ae91 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + solution08_srp + 1.0 + + + 11 + 11 + UTF-8 + + + + + diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..1dd7742a --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/Main.java @@ -0,0 +1,10 @@ +package ru.otus; + +import ru.otus.services.ApplicationRunner; + +// ApplicationRunner, MenuCommandsProcessor, NotesService +public class Main { + public static void main(String[] args) { + new ApplicationRunner().run(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java new file mode 100644 index 00000000..3a3124e9 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException { + public MenuItemIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java new file mode 100644 index 00000000..0747775d --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException { + public NoteIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/model/Note.java b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/model/Note.java new file mode 100644 index 00000000..0bef42ff --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/model/Note.java @@ -0,0 +1,53 @@ +package ru.otus.model; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class Note { + private final String id; + private final LocalDateTime creationTime; + private final String text; + + public Note(String text) { + this.id = UUID.randomUUID().toString(); + this.creationTime = LocalDateTime.now(); + this.text = text; + } + + public Note(String id, String text) { + this.id = id; + this.creationTime = LocalDateTime.now(); + this.text = text; + } + + public Note(String id, LocalDateTime creationTime, String text) { + this.id = id; + this.creationTime = creationTime; + this.text = text; + } + + public static Note of(String text) { + return new Note(text); + } + + public static Note of(String id, String text) { + return new Note(id, text); + } + + public String getId() { + return id; + } + + public LocalDateTime getCreationTime() { + return creationTime; + } + + public String getText() { + return text; + } + + + public Note copy() { + return new Note(id, creationTime, text); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ApplicationRunner.java b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ApplicationRunner.java new file mode 100644 index 00000000..b1dfbda8 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ApplicationRunner.java @@ -0,0 +1,72 @@ +package ru.otus.services; + +import ru.otus.exceptions.MenuItemIndexOutOfBoundsException; +import ru.otus.exceptions.NoteIndexOutOfBoundsException; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class ApplicationRunner { + private static final int MENU_OPTION_SHOW_ALL_NOTES = 1; + private static final int MENU_OPTION_ADD_NEW_NOTE = 2; + private static final int MENU_OPTION_UPDATE_NOTE = 3; + private static final int MENU_OPTION_DELETE_NOTE = 4; + private static final int MENU_OPTION_EXIT = 5; + + private final ConsoleIOService ioService; + private final AtomicBoolean executionFlag; + private final MenuCommandsProcessor commandsProcessor; + + + public ApplicationRunner() { + ioService = new ConsoleIOService(); + executionFlag = new AtomicBoolean(true); + commandsProcessor = new MenuCommandsProcessor(); + + } + + public void run() { + while (executionFlag.get()) { + outputMenu(); + try { + var selectedMenuItem = readSelectedOptionNumber(); + processMenuCommand(selectedMenuItem); + + } catch (NumberFormatException e) { + ioService.outputString("Ошибка при вводе числа"); + } catch (MenuItemIndexOutOfBoundsException e) { + ioService.outputString("Введен неверный номер опции"); + } catch (NoteIndexOutOfBoundsException e) { + ioService.outputString("Введен несуществующий номер заметки"); + } + } + } + + private void outputMenu(){ + ioService.outputString("Выберите одно из следующих действий..."); + ioService.outputString("1. Вывести все заметки"); + ioService.outputString("2. Добавить заметку"); + ioService.outputString("3. Изменить заметку"); + ioService.outputString("4. Удалить заметку"); + ioService.outputString("5. Выйти"); + } + + private void processMenuCommand(int selectedMenuItemIndex) { + if (selectedMenuItemIndex == MENU_OPTION_SHOW_ALL_NOTES) { + commandsProcessor.showAllNotes(ioService); + } else if (selectedMenuItemIndex == MENU_OPTION_ADD_NEW_NOTE) { + commandsProcessor.addNewNote(ioService); + } else if (selectedMenuItemIndex == MENU_OPTION_UPDATE_NOTE) { + commandsProcessor.updateNote(ioService); + } else if (selectedMenuItemIndex == MENU_OPTION_DELETE_NOTE) { + commandsProcessor.deleteNote(ioService); + } else if (selectedMenuItemIndex == MENU_OPTION_EXIT){ + executionFlag.set(false); + } else { + throw new MenuItemIndexOutOfBoundsException("Given menu item index is out of range"); + } + } + + private int readSelectedOptionNumber() { + return ioService.readInt(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ConsoleIOService.java b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ConsoleIOService.java new file mode 100644 index 00000000..06e52546 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ConsoleIOService.java @@ -0,0 +1,29 @@ +package ru.otus.services; + +import java.util.Scanner; + +public class ConsoleIOService { + private final Scanner userInput; + + public ConsoleIOService() { + userInput = new Scanner(System.in); + } + + public void outputString(String s){ + System.out.println(s); + } + + public int readInt(){ + return Integer.parseInt(userInput.nextLine()); + } + + public int readIntWithPrompt(String prompt){ + outputString(prompt); + return Integer.parseInt(userInput.nextLine()); + } + + public String readStringWithPrompt(String prompt){ + outputString(prompt); + return userInput.nextLine(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/MenuCommandsProcessor.java b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/MenuCommandsProcessor.java new file mode 100644 index 00000000..21501f6c --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/MenuCommandsProcessor.java @@ -0,0 +1,58 @@ +package ru.otus.services; + +import ru.otus.model.Note; +import ru.otus.exceptions.NoteIndexOutOfBoundsException; + +import java.util.stream.IntStream; + +public class MenuCommandsProcessor { + private final NotesService notesService; + + public MenuCommandsProcessor() { + notesService = new NotesService(); + } + + public void showAllNotes(ConsoleIOService ioService) { + var noteConverter = new NoteConverter(); + var notes = notesService.getAll(); + ioService.outputString("Заметки:"); + IntStream.range(1, notes.size() + 1) + .mapToObj(k -> noteConverter.convertNoteToString(k, notes.get(k - 1))) + .forEach(ioService::outputString); + ioService.outputString(""); + } + + public void addNewNote(ConsoleIOService ioService) { + var noteText = ioService.readStringWithPrompt("Введите текст заметки..."); + notesService.save(Note.of(noteText)); + } + + public void updateNote(ConsoleIOService ioService) { + var notes = notesService.getAll(); + + var updatedNoteNumber = ioService.readIntWithPrompt("Введите номер изменяемой заметки..."); + checkNoteNumber(updatedNoteNumber, notes.size()); + + var noteText = ioService.readStringWithPrompt("Введите текст заметки..."); + + var updatedNote = notes.get(updatedNoteNumber - 1); + notesService.save(Note.of(updatedNote.getId(), noteText)); + } + + public void deleteNote(ConsoleIOService ioService) { + var notes = notesService.getAll(); + + var deletedNoteNumber = ioService.readIntWithPrompt("Введите номер удаляемой заметки..."); + checkNoteNumber(deletedNoteNumber, notes.size()); + + var updatedNote = notes.get(deletedNoteNumber - 1); + notesService.remove(updatedNote.getId()); + } + + private static void checkNoteNumber(int noteNumber, int notesCount) { + if (noteNumber <= 0 || noteNumber > notesCount) { + throw new NoteIndexOutOfBoundsException("Given number of note is out of range"); + } + } + +} diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NoteConverter.java b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NoteConverter.java new file mode 100644 index 00000000..6c5b57cd --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NoteConverter.java @@ -0,0 +1,9 @@ +package ru.otus.services; + +import ru.otus.model.Note; + +public class NoteConverter { + public String convertNoteToString(int noteNumber, Note note) { + return noteNumber + " | " + note.getCreationTime() + " | " + note.getText(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NotesService.java b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NotesService.java new file mode 100644 index 00000000..438f6bf5 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NotesService.java @@ -0,0 +1,26 @@ +package ru.otus.services; + +import ru.otus.model.Note; + +import java.util.*; +import java.util.stream.Collectors; + +public class NotesService { + private final Map notes; + + public NotesService() { + notes = new HashMap<>(); + } + + public List getAll() { + return notes.values().stream().map(Note::copy).collect(Collectors.toList()); + } + + public void save(Note note) { + notes.put(note.getId(), note); + } + + public void remove(String id) { + notes.remove(id); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/.gitignore b/2022-02/spring-00/architecture-origins/solution09_dip/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/pom.xml b/2022-02/spring-00/architecture-origins/solution09_dip/pom.xml new file mode 100644 index 00000000..2e3b18f9 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + solution09_dip + 1.0 + + + 11 + 11 + UTF-8 + + + + + diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..248c3e5b --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/Main.java @@ -0,0 +1,20 @@ +package ru.otus; + +import ru.otus.config.AppSettings; +import ru.otus.services.*; + +// +AppSettings +public class Main { + public static void main(String[] args) { + var appSettings = new AppSettings(true, "dd.mm.YYYY HH:mm:ss"); + var ioService = new IOServiceStreams(System.out, System.in); + var applicationStopService = new ApplicationStopServiceImpl(ioService, appSettings); + var notesService = new NotesServiceImpl(); + var noteConverter = new NoteConverterImpl(appSettings); + var menuCommandsProcessor = new MenuCommandsProcessorImpl(ioService, notesService, + noteConverter, applicationStopService); + + new ApplicationRunner(ioService, applicationStopService, menuCommandsProcessor) + .run(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/config/AppSettings.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/config/AppSettings.java new file mode 100644 index 00000000..9f393acb --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/config/AppSettings.java @@ -0,0 +1,19 @@ +package ru.otus.config; + +public class AppSettings { + private final boolean confirmExit; + private final String dateTimeFormat; + + public AppSettings(boolean confirmExit, String dateTimeFormat) { + this.confirmExit = confirmExit; + this.dateTimeFormat = dateTimeFormat; + } + + public boolean isConfirmExit() { + return confirmExit; + } + + public String getDateTimeFormat() { + return dateTimeFormat; + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java new file mode 100644 index 00000000..3a3124e9 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException { + public MenuItemIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java new file mode 100644 index 00000000..0747775d --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException { + public NoteIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/model/Note.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/model/Note.java new file mode 100644 index 00000000..0bef42ff --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/model/Note.java @@ -0,0 +1,53 @@ +package ru.otus.model; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class Note { + private final String id; + private final LocalDateTime creationTime; + private final String text; + + public Note(String text) { + this.id = UUID.randomUUID().toString(); + this.creationTime = LocalDateTime.now(); + this.text = text; + } + + public Note(String id, String text) { + this.id = id; + this.creationTime = LocalDateTime.now(); + this.text = text; + } + + public Note(String id, LocalDateTime creationTime, String text) { + this.id = id; + this.creationTime = creationTime; + this.text = text; + } + + public static Note of(String text) { + return new Note(text); + } + + public static Note of(String id, String text) { + return new Note(id, text); + } + + public String getId() { + return id; + } + + public LocalDateTime getCreationTime() { + return creationTime; + } + + public String getText() { + return text; + } + + + public Note copy() { + return new Note(id, creationTime, text); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationRunner.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationRunner.java new file mode 100644 index 00000000..f99a8223 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationRunner.java @@ -0,0 +1,71 @@ +package ru.otus.services; + +import ru.otus.exceptions.MenuItemIndexOutOfBoundsException; +import ru.otus.exceptions.NoteIndexOutOfBoundsException; + +public class ApplicationRunner { + private static final int MENU_OPTION_SHOW_ALL_NOTES = 1; + private static final int MENU_OPTION_ADD_NEW_NOTE = 2; + private static final int MENU_OPTION_UPDATE_NOTE = 3; + private static final int MENU_OPTION_DELETE_NOTE = 4; + private static final int MENU_OPTION_EXIT = 5; + + private final IOService ioService; + private final ApplicationStopService applicationStopService; + private final MenuCommandsProcessor commandsProcessor; + + + public ApplicationRunner(IOService ioService, + ApplicationStopService applicationStopService, + MenuCommandsProcessor commandsProcessor) { + this.ioService = ioService; + this.applicationStopService = applicationStopService; + this.commandsProcessor = commandsProcessor; + } + + public void run() { + while (applicationStopService.isApplicationRunning()) { + outputMenu(); + try { + var selectedMenuItem = readSelectedOptionNumber(); + processMenuCommand(selectedMenuItem); + + } catch (NumberFormatException e) { + ioService.outputString("Ошибка при вводе числа"); + } catch (MenuItemIndexOutOfBoundsException e) { + ioService.outputString("Введен неверный номер опции"); + } catch (NoteIndexOutOfBoundsException e) { + ioService.outputString("Введен несуществующий номер заметки"); + } + } + } + + private void outputMenu(){ + ioService.outputString("Выберите одно из следующих действий..."); + ioService.outputString("1. Вывести все заметки"); + ioService.outputString("2. Добавить заметку"); + ioService.outputString("3. Изменить заметку"); + ioService.outputString("4. Удалить заметку"); + ioService.outputString("5. Выйти"); + } + + private void processMenuCommand(int selectedMenuItemIndex) { + if (selectedMenuItemIndex == MENU_OPTION_SHOW_ALL_NOTES) { + commandsProcessor.showAllNotes(); + } else if (selectedMenuItemIndex == MENU_OPTION_ADD_NEW_NOTE) { + commandsProcessor.addNewNote(); + } else if (selectedMenuItemIndex == MENU_OPTION_UPDATE_NOTE) { + commandsProcessor.updateNote(); + } else if (selectedMenuItemIndex == MENU_OPTION_DELETE_NOTE) { + commandsProcessor.deleteNote(); + } else if (selectedMenuItemIndex == MENU_OPTION_EXIT){ + commandsProcessor.stopApplication(); + } else { + throw new MenuItemIndexOutOfBoundsException("Given menu item index is out of range"); + } + } + + private int readSelectedOptionNumber() { + return ioService.readInt(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopService.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopService.java new file mode 100644 index 00000000..99bde86d --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopService.java @@ -0,0 +1,6 @@ +package ru.otus.services; + +public interface ApplicationStopService { + boolean isApplicationRunning(); + void stopApplication(); +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java new file mode 100644 index 00000000..a3ee6f39 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java @@ -0,0 +1,34 @@ +package ru.otus.services; + +import ru.otus.config.AppSettings; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class ApplicationStopServiceImpl implements ApplicationStopService { + + private final IOService ioService; + private final AppSettings appSettings; + private final AtomicBoolean executionFlag; + + public ApplicationStopServiceImpl(IOService ioService, AppSettings appSettings) { + this.ioService = ioService; + this.appSettings = appSettings; + this.executionFlag = new AtomicBoolean(true); + } + + @Override + public boolean isApplicationRunning() { + return executionFlag.get(); + } + + @Override + public void stopApplication() { + if (appSettings.isConfirmExit()) { + var exitConfirmation = ioService.readStringWithPrompt("Действительно выйти? (да/нет)"); + if (exitConfirmation.equalsIgnoreCase("нет")) { + return; + } + } + executionFlag.set(false); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOService.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOService.java new file mode 100644 index 00000000..be073ce7 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOService.java @@ -0,0 +1,11 @@ +package ru.otus.services; + +public interface IOService { + void outputString(String s); + + int readInt(); + + int readIntWithPrompt(String prompt); + + String readStringWithPrompt(String prompt); +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOServiceStreams.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOServiceStreams.java new file mode 100644 index 00000000..f23d9fc0 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOServiceStreams.java @@ -0,0 +1,37 @@ +package ru.otus.services; + +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Scanner; + +public class IOServiceStreams implements IOService { + private final PrintStream output; + private final Scanner input; + + public IOServiceStreams(PrintStream outputStream, InputStream inputStream) { + output = outputStream; + input = new Scanner(inputStream); + } + + @Override + public void outputString(String s){ + output.println(s); + } + + @Override + public int readInt(){ + return Integer.parseInt(input.nextLine()); + } + + @Override + public int readIntWithPrompt(String prompt){ + outputString(prompt); + return Integer.parseInt(input.nextLine()); + } + + @Override + public String readStringWithPrompt(String prompt){ + outputString(prompt); + return input.nextLine(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessor.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessor.java new file mode 100644 index 00000000..a624d49a --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessor.java @@ -0,0 +1,13 @@ +package ru.otus.services; + +public interface MenuCommandsProcessor { + void showAllNotes(); + + void addNewNote(); + + void updateNote(); + + void deleteNote(); + + void stopApplication(); +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessorImpl.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessorImpl.java new file mode 100644 index 00000000..86132fe8 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessorImpl.java @@ -0,0 +1,74 @@ +package ru.otus.services; + +import ru.otus.model.Note; +import ru.otus.exceptions.NoteIndexOutOfBoundsException; + +import java.util.stream.IntStream; + +public class MenuCommandsProcessorImpl implements MenuCommandsProcessor { + private final IOService ioService; + private final NotesService notesService; + private final NoteConverter noteConverter; + private final ApplicationStopService applicationStopService; + + public MenuCommandsProcessorImpl(IOService ioService, NotesService notesService, + NoteConverter noteConverter, + ApplicationStopService applicationStopService) { + this.ioService = ioService; + this.notesService = notesService; + this.noteConverter = noteConverter; + this.applicationStopService = applicationStopService; + } + + @Override + public void showAllNotes() { + var notes = notesService.getAll(); + ioService.outputString("Заметки:"); + IntStream.range(1, notes.size() + 1) + .mapToObj(k -> noteConverter.convertNoteToString(k, notes.get(k - 1))) + .forEach(ioService::outputString); + ioService.outputString(""); + } + + @Override + public void addNewNote() { + var noteText = ioService.readStringWithPrompt("Введите текст заметки..."); + notesService.save(Note.of(noteText)); + } + + @Override + public void updateNote() { + var notes = notesService.getAll(); + + var updatedNoteNumber = ioService.readIntWithPrompt("Введите номер изменяемой заметки..."); + checkNoteNumber(updatedNoteNumber, notes.size()); + + var noteText = ioService.readStringWithPrompt("Введите текст заметки..."); + + var updatedNote = notes.get(updatedNoteNumber - 1); + notesService.save(Note.of(updatedNote.getId(), noteText)); + } + + @Override + public void deleteNote() { + var notes = notesService.getAll(); + + var deletedNoteNumber = ioService.readIntWithPrompt("Введите номер удаляемой заметки..."); + checkNoteNumber(deletedNoteNumber, notes.size()); + + var updatedNote = notes.get(deletedNoteNumber - 1); + notesService.remove(updatedNote.getId()); + } + + @Override + public void stopApplication() { + applicationStopService.stopApplication(); + } + + private static void checkNoteNumber(int noteNumber, int notesCount) { + if (noteNumber <= 0 || noteNumber > notesCount) { + throw new NoteIndexOutOfBoundsException("Given number of note is out of range"); + } + } + +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverter.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverter.java new file mode 100644 index 00000000..067ffca1 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverter.java @@ -0,0 +1,7 @@ +package ru.otus.services; + +import ru.otus.model.Note; + +public interface NoteConverter { + String convertNoteToString(int noteNumber, Note note); +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverterImpl.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverterImpl.java new file mode 100644 index 00000000..9336ef1b --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverterImpl.java @@ -0,0 +1,21 @@ +package ru.otus.services; + +import ru.otus.config.AppSettings; +import ru.otus.model.Note; + + +import java.time.format.DateTimeFormatter; + +public class NoteConverterImpl implements NoteConverter { + private final AppSettings appSettings; + + public NoteConverterImpl(AppSettings appSettings) { + this.appSettings = appSettings; + } + + @Override + public String convertNoteToString(int noteNumber, Note note) { + var dateTimeFormatter = DateTimeFormatter.ofPattern(appSettings.getDateTimeFormat()); + return noteNumber + " | " + dateTimeFormatter.format(note.getCreationTime()) + " | " + note.getText(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesService.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesService.java new file mode 100644 index 00000000..03c22f0a --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesService.java @@ -0,0 +1,13 @@ +package ru.otus.services; + +import ru.otus.model.Note; + +import java.util.List; + +public interface NotesService { + List getAll(); + + void save(Note note); + + void remove(String id); +} diff --git a/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesServiceImpl.java b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesServiceImpl.java new file mode 100644 index 00000000..9722bb26 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesServiceImpl.java @@ -0,0 +1,31 @@ +package ru.otus.services; + +import ru.otus.model.Note; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class NotesServiceImpl implements NotesService { + private final Map notes; + + public NotesServiceImpl() { + notes = new HashMap<>(); + } + + @Override + public List getAll() { + return notes.values().stream().map(Note::copy).collect(Collectors.toList()); + } + + @Override + public void save(Note note) { + notes.put(note.getId(), note); + } + + @Override + public void remove(String id) { + notes.remove(id); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/.gitignore b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/pom.xml b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/pom.xml new file mode 100644 index 00000000..3ce8c35e --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + ru.otus + solution10_ocp_isp + 1.0 + + + 11 + 11 + UTF-8 + 5.8.2 + 4.1.0 + 3.21.0 + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + + org.junit.jupiter + junit-jupiter-params + ${junit.jupiter.version} + test + + + + + org.mockito + mockito-core + ${mockito.version} + test + + + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/Main.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/Main.java new file mode 100644 index 00000000..fa64a163 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/Main.java @@ -0,0 +1,44 @@ +package ru.otus; + +import ru.otus.config.AppSettings; +import ru.otus.services.*; +import ru.otus.services.processors.*; +import ru.otus.services.menu.MenuOption; +import ru.otus.services.menu.MenuOptionsRegistryImpl; + + +import java.util.List; + +// OCP + ISP (AppSettings + IOService) + тесты +public class Main { + public static void main(String[] args) { + var appSettings = new AppSettings(true, "dd.mm.YYYY HH:mm:ss"); + var ioService = new IOServiceStreams(System.out, System.in); + var applicationStopService = new ApplicationStopServiceImpl(ioService, appSettings); + var notesService = new NotesServiceImpl(); + var noteConverter = new NoteConverterImpl(appSettings); + + var menuOptions = List.of( + new MenuOption(1, "Вывести все заметки"), + new MenuOption(2, "Добавить заметку"), + new MenuOption(3, "Изменить заметку"), + new MenuOption(4, "Удалить заметку"), + new MenuOption(5, "Выйти") + + ); + var menuOptionsRegistry = new MenuOptionsRegistryImpl(menuOptions); + + var processors = List.of( + new ShowAllNotesSingleCommandProcessor(ioService, notesService, noteConverter, menuOptions.get(0)), + new AddNewNoteSingleCommandProcessor(ioService, notesService, menuOptions.get(1)), + new UpdateNoteSingleCommandProcessor(ioService, notesService, menuOptions.get(2)), + new DeleteNoteSingleCommandProcessor(ioService, notesService, menuOptions.get(3)), + new StopApplicationSingleCommandProcessor(applicationStopService, menuOptions.get(4)) + ); + + var menuCommandsProcessor = new MenuCommandsProcessorImpl(processors); + + new ApplicationRunner(ioService, applicationStopService, menuOptionsRegistry, menuCommandsProcessor) + .run(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/AppSettings.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/AppSettings.java new file mode 100644 index 00000000..581b214e --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/AppSettings.java @@ -0,0 +1,21 @@ +package ru.otus.config; + +public class AppSettings implements DateTimeFormatProvider, ApplicationStopServiceSettingsProvider{ + private final boolean confirmExit; + private final String dateTimeFormat; + + public AppSettings(boolean confirmExit, String dateTimeFormat) { + this.confirmExit = confirmExit; + this.dateTimeFormat = dateTimeFormat; + } + + @Override + public boolean isConfirmExit() { + return confirmExit; + } + + @Override + public String getDateTimeFormat() { + return dateTimeFormat; + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/ApplicationStopServiceSettingsProvider.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/ApplicationStopServiceSettingsProvider.java new file mode 100644 index 00000000..ccbeafa7 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/ApplicationStopServiceSettingsProvider.java @@ -0,0 +1,5 @@ +package ru.otus.config; + +public interface ApplicationStopServiceSettingsProvider { + boolean isConfirmExit(); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/DateTimeFormatProvider.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/DateTimeFormatProvider.java new file mode 100644 index 00000000..b0e72ec0 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/DateTimeFormatProvider.java @@ -0,0 +1,5 @@ +package ru.otus.config; + +public interface DateTimeFormatProvider { + String getDateTimeFormat(); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuCommandProcessorNotFound.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuCommandProcessorNotFound.java new file mode 100644 index 00000000..a2057a42 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuCommandProcessorNotFound.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class MenuCommandProcessorNotFound extends RuntimeException { + public MenuCommandProcessorNotFound(String message) { + super(message); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java new file mode 100644 index 00000000..3a3124e9 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException { + public MenuItemIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java new file mode 100644 index 00000000..0747775d --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java @@ -0,0 +1,7 @@ +package ru.otus.exceptions; + +public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException { + public NoteIndexOutOfBoundsException(String s) { + super(s); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/model/Note.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/model/Note.java new file mode 100644 index 00000000..0bef42ff --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/model/Note.java @@ -0,0 +1,53 @@ +package ru.otus.model; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class Note { + private final String id; + private final LocalDateTime creationTime; + private final String text; + + public Note(String text) { + this.id = UUID.randomUUID().toString(); + this.creationTime = LocalDateTime.now(); + this.text = text; + } + + public Note(String id, String text) { + this.id = id; + this.creationTime = LocalDateTime.now(); + this.text = text; + } + + public Note(String id, LocalDateTime creationTime, String text) { + this.id = id; + this.creationTime = creationTime; + this.text = text; + } + + public static Note of(String text) { + return new Note(text); + } + + public static Note of(String id, String text) { + return new Note(id, text); + } + + public String getId() { + return id; + } + + public LocalDateTime getCreationTime() { + return creationTime; + } + + public String getText() { + return text; + } + + + public Note copy() { + return new Note(id, creationTime, text); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationRunner.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationRunner.java new file mode 100644 index 00000000..aebf7b88 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationRunner.java @@ -0,0 +1,66 @@ +package ru.otus.services; + +import ru.otus.exceptions.MenuCommandProcessorNotFound; +import ru.otus.exceptions.MenuItemIndexOutOfBoundsException; +import ru.otus.exceptions.NoteIndexOutOfBoundsException; +import ru.otus.services.menu.MenuOption; +import ru.otus.services.menu.MenuOptionsRegistry; +import ru.otus.services.processors.MenuCommandsProcessor; + +import java.util.Comparator; + +public class ApplicationRunner { + private final IOService ioService; + private final ApplicationStopService applicationStopService; + private final MenuOptionsRegistry menuOptionsRegistry; + private final MenuCommandsProcessor commandsProcessor; + + + public ApplicationRunner(IOService ioService, + ApplicationStopService applicationStopService, + MenuOptionsRegistry menuOptionsRegistry, + MenuCommandsProcessor commandsProcessor) { + this.ioService = ioService; + this.applicationStopService = applicationStopService; + this.menuOptionsRegistry = menuOptionsRegistry; + this.commandsProcessor = commandsProcessor; + } + + public void run() { + while (applicationStopService.isApplicationRunning()) { + outputMenu(); + try { + var selectedMenuItem = readSelectedOptionNumber(); + processMenuCommand(selectedMenuItem); + + } catch (NumberFormatException e) { + ioService.outputString("Ошибка при вводе числа"); + } catch (MenuItemIndexOutOfBoundsException e) { + ioService.outputString("Введен неверный номер опции"); + } catch (NoteIndexOutOfBoundsException e) { + ioService.outputString("Введен несуществующий номер заметки"); + } catch (MenuCommandProcessorNotFound e) { + ioService.outputString("Не найден обработчик для выбранного пункта меню"); + } + } + } + + private void outputMenu() { + ioService.outputString("Выберите одно из следующих действий..."); + menuOptionsRegistry.getAvailableMenuOptions().stream() + .sorted(Comparator.comparingInt(MenuOption::getId)) + .map(m -> m.getId() + ". " + m.getDescription()) + .forEach(ioService::outputString); + } + + private void processMenuCommand(int selectedMenuItemId) { + var selectedMenuOption = menuOptionsRegistry.getMenuOptionById(selectedMenuItemId) + .orElseThrow(() -> new MenuItemIndexOutOfBoundsException("Given menu item index is out of range")); + + commandsProcessor.processMenuCommand(selectedMenuOption); + } + + private int readSelectedOptionNumber() { + return ioService.readInt(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopService.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopService.java new file mode 100644 index 00000000..99bde86d --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopService.java @@ -0,0 +1,6 @@ +package ru.otus.services; + +public interface ApplicationStopService { + boolean isApplicationRunning(); + void stopApplication(); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java new file mode 100644 index 00000000..228ea8d4 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java @@ -0,0 +1,36 @@ +package ru.otus.services; + +import ru.otus.config.ApplicationStopServiceSettingsProvider; +import ru.otus.services.processors.InputService; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class ApplicationStopServiceImpl implements ApplicationStopService { + + private final InputService inputService; + private final ApplicationStopServiceSettingsProvider settingsProvider; + private final AtomicBoolean executionFlag; + + public ApplicationStopServiceImpl(InputService inputService, + ApplicationStopServiceSettingsProvider settingsProvider) { + this.inputService = inputService; + this.settingsProvider = settingsProvider; + this.executionFlag = new AtomicBoolean(true); + } + + @Override + public boolean isApplicationRunning() { + return executionFlag.get(); + } + + @Override + public void stopApplication() { + if (settingsProvider.isConfirmExit()) { + var exitConfirmation = inputService.readStringWithPrompt("Действительно выйти? (да/нет)"); + if (exitConfirmation.equalsIgnoreCase("нет")) { + return; + } + } + executionFlag.set(false); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOService.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOService.java new file mode 100644 index 00000000..4206d177 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOService.java @@ -0,0 +1,9 @@ +package ru.otus.services; + +import ru.otus.services.processors.InputService; +import ru.otus.services.processors.OutputService; + +public interface IOService extends InputService, OutputService { + + +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOServiceStreams.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOServiceStreams.java new file mode 100644 index 00000000..f23d9fc0 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOServiceStreams.java @@ -0,0 +1,37 @@ +package ru.otus.services; + +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Scanner; + +public class IOServiceStreams implements IOService { + private final PrintStream output; + private final Scanner input; + + public IOServiceStreams(PrintStream outputStream, InputStream inputStream) { + output = outputStream; + input = new Scanner(inputStream); + } + + @Override + public void outputString(String s){ + output.println(s); + } + + @Override + public int readInt(){ + return Integer.parseInt(input.nextLine()); + } + + @Override + public int readIntWithPrompt(String prompt){ + outputString(prompt); + return Integer.parseInt(input.nextLine()); + } + + @Override + public String readStringWithPrompt(String prompt){ + outputString(prompt); + return input.nextLine(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverter.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverter.java new file mode 100644 index 00000000..067ffca1 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverter.java @@ -0,0 +1,7 @@ +package ru.otus.services; + +import ru.otus.model.Note; + +public interface NoteConverter { + String convertNoteToString(int noteNumber, Note note); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverterImpl.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverterImpl.java new file mode 100644 index 00000000..0361e271 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverterImpl.java @@ -0,0 +1,20 @@ +package ru.otus.services; + +import ru.otus.config.DateTimeFormatProvider; +import ru.otus.model.Note; + +import java.time.format.DateTimeFormatter; + +public class NoteConverterImpl implements NoteConverter { + private final DateTimeFormatProvider dateTimeFormatProvider; + + public NoteConverterImpl(DateTimeFormatProvider dateTimeFormatProvider) { + this.dateTimeFormatProvider = dateTimeFormatProvider; + } + + @Override + public String convertNoteToString(int noteNumber, Note note) { + var dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormatProvider.getDateTimeFormat()); + return noteNumber + " | " + dateTimeFormatter.format(note.getCreationTime()) + " | " + note.getText(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesService.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesService.java new file mode 100644 index 00000000..03c22f0a --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesService.java @@ -0,0 +1,13 @@ +package ru.otus.services; + +import ru.otus.model.Note; + +import java.util.List; + +public interface NotesService { + List getAll(); + + void save(Note note); + + void remove(String id); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesServiceImpl.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesServiceImpl.java new file mode 100644 index 00000000..9722bb26 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesServiceImpl.java @@ -0,0 +1,31 @@ +package ru.otus.services; + +import ru.otus.model.Note; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class NotesServiceImpl implements NotesService { + private final Map notes; + + public NotesServiceImpl() { + notes = new HashMap<>(); + } + + @Override + public List getAll() { + return notes.values().stream().map(Note::copy).collect(Collectors.toList()); + } + + @Override + public void save(Note note) { + notes.put(note.getId(), note); + } + + @Override + public void remove(String id) { + notes.remove(id); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOption.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOption.java new file mode 100644 index 00000000..d325bc19 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOption.java @@ -0,0 +1,34 @@ +package ru.otus.services.menu; + +public class MenuOption { + private final int id; + private final String description; + + public MenuOption(int id, String description) { + this.id = id; + this.description = description; + } + + public int getId() { + return id; + } + + public String getDescription() { + return description; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MenuOption that = (MenuOption) o; + + return id == that.id; + } + + @Override + public int hashCode() { + return id; + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistry.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistry.java new file mode 100644 index 00000000..e0182d0e --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistry.java @@ -0,0 +1,9 @@ +package ru.otus.services.menu; + +import java.util.List; +import java.util.Optional; + +public interface MenuOptionsRegistry { + List getAvailableMenuOptions(); + Optional getMenuOptionById(int id); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistryImpl.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistryImpl.java new file mode 100644 index 00000000..da8c2410 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistryImpl.java @@ -0,0 +1,26 @@ +package ru.otus.services.menu; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class MenuOptionsRegistryImpl implements MenuOptionsRegistry { + private final Map options; + + public MenuOptionsRegistryImpl(List options) { + this.options = options.stream() + .collect(Collectors.toUnmodifiableMap(MenuOption::getId, Function.identity())); + } + + @Override + public List getAvailableMenuOptions() { + return options.values().stream().collect(Collectors.toUnmodifiableList()); + } + + @Override + public Optional getMenuOptionById(int id) { + return Optional.ofNullable(options.get(id)); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessor.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessor.java new file mode 100644 index 00000000..e79b67df --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessor.java @@ -0,0 +1,30 @@ +package ru.otus.services.processors; + +import ru.otus.model.Note; +import ru.otus.services.NotesService; +import ru.otus.services.menu.MenuOption; + +public class AddNewNoteSingleCommandProcessor implements MenuSingleCommandProcessor { + private final MenuOption processedCommandOption; + private final InputService inputService; + private final NotesService notesService; + + public AddNewNoteSingleCommandProcessor(InputService inputService, NotesService notesService, + MenuOption processedCommandOption) { + this.inputService = inputService; + this.notesService = notesService; + this.processedCommandOption = processedCommandOption; + } + + @Override + public void processCommand() { + var noteText = inputService.readStringWithPrompt("Введите текст заметки..."); + notesService.save(Note.of(noteText)); + + } + + @Override + public MenuOption getProcessedCommandOption() { + return processedCommandOption; + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/DeleteNoteSingleCommandProcessor.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/DeleteNoteSingleCommandProcessor.java new file mode 100644 index 00000000..941e7756 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/DeleteNoteSingleCommandProcessor.java @@ -0,0 +1,37 @@ +package ru.otus.services.processors; + +import ru.otus.services.NotesService; +import ru.otus.services.menu.MenuOption; + + +import static ru.otus.services.processors.utils.NotesListUtil.checkNoteNumber; + +public class DeleteNoteSingleCommandProcessor implements MenuSingleCommandProcessor { + private final MenuOption processedCommandOption; + private final InputService inputService; + private final NotesService notesService; + + public DeleteNoteSingleCommandProcessor(InputService inputService, NotesService notesService, + MenuOption processedCommandOption) { + this.inputService = inputService; + this.notesService = notesService; + this.processedCommandOption = processedCommandOption; + } + + @Override + public void processCommand() { + var notes = notesService.getAll(); + + var deletedNoteNumber = inputService.readIntWithPrompt("Введите номер удаляемой заметки..."); + checkNoteNumber(deletedNoteNumber, notes.size()); + + var updatedNote = notes.get(deletedNoteNumber - 1); + notesService.remove(updatedNote.getId()); + } + + @Override + public MenuOption getProcessedCommandOption() { + return processedCommandOption; + } + +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/InputService.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/InputService.java new file mode 100644 index 00000000..be6485d3 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/InputService.java @@ -0,0 +1,9 @@ +package ru.otus.services.processors; + +public interface InputService { + int readInt(); + + int readIntWithPrompt(String prompt); + + String readStringWithPrompt(String prompt); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessor.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessor.java new file mode 100644 index 00000000..272a1128 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessor.java @@ -0,0 +1,7 @@ +package ru.otus.services.processors; + +import ru.otus.services.menu.MenuOption; + +public interface MenuCommandsProcessor { + void processMenuCommand(MenuOption selectedMenuOption); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessorImpl.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessorImpl.java new file mode 100644 index 00000000..930e5b20 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessorImpl.java @@ -0,0 +1,27 @@ +package ru.otus.services.processors; + +import ru.otus.exceptions.MenuCommandProcessorNotFound; +import ru.otus.services.menu.MenuOption; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class MenuCommandsProcessorImpl implements MenuCommandsProcessor { + private final Map processors; + + public MenuCommandsProcessorImpl(List processors) { + this.processors = processors.stream() + .collect(Collectors.toMap(MenuSingleCommandProcessor::getProcessedCommandOption, Function.identity())); + } + + @Override + public void processMenuCommand(MenuOption selectedMenuOption) { + var commandProcessor = processors.get(selectedMenuOption); + if (commandProcessor == null) { + throw new MenuCommandProcessorNotFound("Menu command processor for given option does not registered"); + } + commandProcessor.processCommand(); + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuSingleCommandProcessor.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuSingleCommandProcessor.java new file mode 100644 index 00000000..697b1367 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuSingleCommandProcessor.java @@ -0,0 +1,8 @@ +package ru.otus.services.processors; + +import ru.otus.services.menu.MenuOption; + +public interface MenuSingleCommandProcessor { + void processCommand(); + MenuOption getProcessedCommandOption(); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/OutputService.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/OutputService.java new file mode 100644 index 00000000..75153cb7 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/OutputService.java @@ -0,0 +1,5 @@ +package ru.otus.services.processors; + +public interface OutputService { + void outputString(String s); +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/ShowAllNotesSingleCommandProcessor.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/ShowAllNotesSingleCommandProcessor.java new file mode 100644 index 00000000..c71dfa3a --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/ShowAllNotesSingleCommandProcessor.java @@ -0,0 +1,38 @@ +package ru.otus.services.processors; + +import ru.otus.services.NoteConverter; +import ru.otus.services.NotesService; +import ru.otus.services.menu.MenuOption; + +import java.util.stream.IntStream; + +public class ShowAllNotesSingleCommandProcessor implements MenuSingleCommandProcessor { + private final MenuOption processedCommandOption; + + private final OutputService outputService; + private final NotesService notesService; + private final NoteConverter noteConverter; + + public ShowAllNotesSingleCommandProcessor(OutputService outputService, NotesService notesService, + NoteConverter noteConverter, MenuOption processedCommandOption) { + this.outputService = outputService; + this.notesService = notesService; + this.noteConverter = noteConverter; + this.processedCommandOption = processedCommandOption; + } + + @Override + public void processCommand() { + var notes = notesService.getAll(); + outputService.outputString("Заметки:"); + IntStream.range(1, notes.size() + 1) + .mapToObj(k -> noteConverter.convertNoteToString(k, notes.get(k - 1))) + .forEach(outputService::outputString); + outputService.outputString(""); + } + + @Override + public MenuOption getProcessedCommandOption() { + return processedCommandOption; + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/StopApplicationSingleCommandProcessor.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/StopApplicationSingleCommandProcessor.java new file mode 100644 index 00000000..25e4a67a --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/StopApplicationSingleCommandProcessor.java @@ -0,0 +1,25 @@ +package ru.otus.services.processors; + +import ru.otus.services.ApplicationStopService; +import ru.otus.services.menu.MenuOption; + +public class StopApplicationSingleCommandProcessor implements MenuSingleCommandProcessor { + private final MenuOption processedCommandOption; + private final ApplicationStopService applicationStopService; + + public StopApplicationSingleCommandProcessor(ApplicationStopService applicationStopService, + MenuOption processedCommandOption) { + this.applicationStopService = applicationStopService; + this.processedCommandOption = processedCommandOption; + } + + @Override + public void processCommand() { + applicationStopService.stopApplication(); + } + + @Override + public MenuOption getProcessedCommandOption() { + return processedCommandOption; + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/UpdateNoteSingleCommandProcessor.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/UpdateNoteSingleCommandProcessor.java new file mode 100644 index 00000000..ca5dc617 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/UpdateNoteSingleCommandProcessor.java @@ -0,0 +1,41 @@ +package ru.otus.services.processors; + +import ru.otus.model.Note; +import ru.otus.services.menu.MenuOption; +import ru.otus.services.NotesService; + +import java.util.List; + +import static ru.otus.services.processors.utils.NotesListUtil.checkNoteNumber; + +public class UpdateNoteSingleCommandProcessor implements MenuSingleCommandProcessor { + private final MenuOption processedCommandOption; + private final InputService inputService; + private final NotesService notesService; + + public UpdateNoteSingleCommandProcessor(InputService inputService, NotesService notesService, + MenuOption processedCommandOption) { + this.inputService = inputService; + this.notesService = notesService; + this.processedCommandOption = processedCommandOption; + } + + @Override + public void processCommand() { + var notes = notesService.getAll(); + + var updatedNoteNumber = inputService.readIntWithPrompt("Введите номер изменяемой заметки..."); + checkNoteNumber(updatedNoteNumber, notes.size()); + + var noteText = inputService.readStringWithPrompt("Введите текст заметки..."); + + var updatedNote = notes.get(updatedNoteNumber - 1); + notesService.save(Note.of(updatedNote.getId(), noteText)); } + + @Override + public MenuOption getProcessedCommandOption() { + return processedCommandOption; + } + + +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/utils/NotesListUtil.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/utils/NotesListUtil.java new file mode 100644 index 00000000..4c658e21 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/utils/NotesListUtil.java @@ -0,0 +1,11 @@ +package ru.otus.services.processors.utils; + +import ru.otus.exceptions.NoteIndexOutOfBoundsException; + +public class NotesListUtil { + public static void checkNoteNumber(int noteNumber, int notesCount) { + if (noteNumber <= 0 || noteNumber > notesCount) { + throw new NoteIndexOutOfBoundsException("Given number of note is out of range"); + } + } +} diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/ApplicationStopServiceImplTest.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/ApplicationStopServiceImplTest.java new file mode 100644 index 00000000..3447c600 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/ApplicationStopServiceImplTest.java @@ -0,0 +1,33 @@ +package ru.otus.services; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import ru.otus.config.ApplicationStopServiceSettingsProvider; +import ru.otus.services.processors.InputService; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; + +@DisplayName("Сервис остановки приложения ") +class ApplicationStopServiceImplTest { + + @DisplayName("должен возвращать корректный статус работы приложения") + @ParameterizedTest(name = "остановить приложение: {0}, ожидаемый результат: {1}") + @CsvSource({"false, true", "true, false"}) + void shouldReturnCorrectApplicationExecutionStatus(boolean shouldStopApplication, boolean expectedStatus) { + var inputService = mock(InputService.class); + var settingsProvider = mock(ApplicationStopServiceSettingsProvider.class); + given(settingsProvider.isConfirmExit()).willReturn(false); + var applicationStopService = new ApplicationStopServiceImpl(inputService, settingsProvider); + + if (shouldStopApplication) { + applicationStopService.stopApplication(); + verify(settingsProvider, times(1)).isConfirmExit(); + } + var actualStatus = applicationStopService.isApplicationRunning(); + assertThat(actualStatus).isEqualTo(expectedStatus); + verify(inputService, never()).readStringWithPrompt(any()); + } +} \ No newline at end of file diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/menu/MenuOptionsRegistryImplTest.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/menu/MenuOptionsRegistryImplTest.java new file mode 100644 index 00000000..a9265ae6 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/menu/MenuOptionsRegistryImplTest.java @@ -0,0 +1,43 @@ +package ru.otus.services.menu; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("Реестр опций меню ") +class MenuOptionsRegistryImplTest { + + private List options; + private MenuOptionsRegistryImpl menuOptionsRegistry; + + @BeforeEach + void setUp() { + options = List.of(new MenuOption(1, "opt1"), new MenuOption(2, "opt2")); + menuOptionsRegistry = new MenuOptionsRegistryImpl(options); + } + + @DisplayName("должен возвращать список ожидаемых опций ") + @Test + void shouldReturnExpectedAvailableMenuOptions() { + var actualOptions = menuOptionsRegistry.getAvailableMenuOptions(); + assertThat(actualOptions) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrderElementsOf(options); + } + + @DisplayName("должен корректно возвращать опцию меню по ее идентификатору ") + @Test + void shouldReturnExpectedMenuOptionById() { + var expectedOption = options.get(0); + var actualOption = menuOptionsRegistry.getMenuOptionById(expectedOption.getId()); + assertThat(actualOption).isNotEmpty() + .get() + .usingRecursiveComparison() + .isEqualTo(expectedOption); + } +} \ No newline at end of file diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessorTest.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessorTest.java new file mode 100644 index 00000000..bfd50831 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessorTest.java @@ -0,0 +1,60 @@ +package ru.otus.services.processors; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import ru.otus.model.Note; +import ru.otus.services.NotesService; +import ru.otus.services.menu.MenuOption; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@DisplayName("Процессор команды добавления новой заметки ") +@ExtendWith(MockitoExtension.class) +class AddNewNoteSingleCommandProcessorTest { + + @Mock + private MenuOption processedCommandOption; + + @Mock + private InputService inputService; + + @Mock + private NotesService notesService; + + @InjectMocks + private AddNewNoteSingleCommandProcessor processor; + + @DisplayName("должен корректно добавлять новой заметки ") + @Test + void shouldCorrectAddExpectedNoteUseExpectedServicesMethods() { + var expectedNoteText = "Expected Note Text"; + given(inputService.readStringWithPrompt(anyString())) + .willReturn(expectedNoteText); + + processor.processCommand(); + + verify(inputService, times(1)).readStringWithPrompt(any()); + + var captor = ArgumentCaptor.forClass(Note.class); + verify(notesService).save(captor.capture()); + var actualNote = captor.getValue(); + assertThat(actualNote).extracting(Note::getText).isEqualTo(expectedNoteText); + + } + + @DisplayName("должен возвращает ожидаемый тип обрабатываемой команды") + @Test + void shouldReturnExpectedProcessedCommandOption() { + assertThat(processor.getProcessedCommandOption()).isEqualTo(processedCommandOption); + } +} \ No newline at end of file diff --git a/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/MenuCommandsProcessorImplTest.java b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/MenuCommandsProcessorImplTest.java new file mode 100644 index 00000000..f8aca601 --- /dev/null +++ b/2022-02/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/MenuCommandsProcessorImplTest.java @@ -0,0 +1,50 @@ +package ru.otus.services.processors; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import ru.otus.exceptions.MenuCommandProcessorNotFound; +import ru.otus.services.menu.MenuOption; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; + +@DisplayName("Процессор команд меню ") +class MenuCommandsProcessorImplTest { + + private List singleCommandProcessors; + private MenuCommandsProcessorImpl processor; + + @BeforeEach + void setUp() { + singleCommandProcessors = new ArrayList<>(3); + for (int i = 1; i < 4; i++) { + var opt = new MenuOption(i, "opt" + i); + var singleCommandProcessor = mock(MenuSingleCommandProcessor.class); + given(singleCommandProcessor.getProcessedCommandOption()).willReturn(opt); + singleCommandProcessors.add(singleCommandProcessor); + } + processor = new MenuCommandsProcessorImpl(singleCommandProcessors); + } + + @DisplayName("должен корректно обрабатывать команду если для нее есть процессор") + @Test + void shouldCorrectProcessMenuCommandWhenProcessorForGivenCommandExists() { + for (var singleCommandProcessor: singleCommandProcessors) { + processor.processMenuCommand(singleCommandProcessor.getProcessedCommandOption()); + verify(singleCommandProcessor, times(1)).processCommand(); + } + } + + @DisplayName("должен кидать ожидаемое исключение если процессор для заданной команды отсутствует") + @Test + void shouldThrowExpectedExceptionWhenProcessorForGivenCommandDoesNotExists() { + var commandOptionWithNotExistingProcessor = new MenuOption(4, "opt4"); + assertThatCode(() -> processor.processMenuCommand(commandOptionWithNotExistingProcessor)) + .isInstanceOf(MenuCommandProcessorNotFound.class); + } +} \ No newline at end of file