diff --git a/2022-05/spring-00/architecture-origins/.gitignore b/2022-05/spring-00/architecture-origins/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/pom.xml b/2022-05/spring-00/architecture-origins/pom.xml
new file mode 100644
index 00000000..0890067c
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution01/.gitignore b/2022-05/spring-00/architecture-origins/solution01/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution01/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution01/pom.xml b/2022-05/spring-00/architecture-origins/solution01/pom.xml
new file mode 100644
index 00000000..c76439cc
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution01/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution01/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..45b177a2
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution02_codeface/.gitignore b/2022-05/spring-00/architecture-origins/solution02_codeface/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution02_codeface/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution02_codeface/pom.xml b/2022-05/spring-00/architecture-origins/solution02_codeface/pom.xml
new file mode 100644
index 00000000..85e56d77
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution02_codeface/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution02_codeface/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..ea54bae3
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution03_kiss/.gitignore b/2022-05/spring-00/architecture-origins/solution03_kiss/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution03_kiss/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution03_kiss/pom.xml b/2022-05/spring-00/architecture-origins/solution03_kiss/pom.xml
new file mode 100644
index 00000000..08d5b82d
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution03_kiss/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution03_kiss/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..5365056a
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution03_kiss/src/main/java/ru/otus/Main.java
@@ -0,0 +1,104 @@
+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-05/spring-00/architecture-origins/solution04_kiss/.gitignore b/2022-05/spring-00/architecture-origins/solution04_kiss/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution04_kiss/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution04_kiss/pom.xml b/2022-05/spring-00/architecture-origins/solution04_kiss/pom.xml
new file mode 100644
index 00000000..7f365752
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution04_kiss/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution04_kiss/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..9420d5a4
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution05_dry/.gitignore b/2022-05/spring-00/architecture-origins/solution05_dry/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution05_dry/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution05_dry/pom.xml b/2022-05/spring-00/architecture-origins/solution05_dry/pom.xml
new file mode 100644
index 00000000..1eaafeec
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution05_dry/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution05_dry/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..bf81db2c
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution06_dry/.gitignore b/2022-05/spring-00/architecture-origins/solution06_dry/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution06_dry/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution06_dry/pom.xml b/2022-05/spring-00/architecture-origins/solution06_dry/pom.xml
new file mode 100644
index 00000000..292cf7ec
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..d652ca12
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java
new file mode 100644
index 00000000..5fa79742
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution06_dry/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java
new file mode 100644
index 00000000..2c9592f6
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution07_srp/.gitignore b/2022-05/spring-00/architecture-origins/solution07_srp/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution07_srp/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution07_srp/pom.xml b/2022-05/spring-00/architecture-origins/solution07_srp/pom.xml
new file mode 100644
index 00000000..4f3ab886
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..99416733
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java
new file mode 100644
index 00000000..5fa79742
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java
new file mode 100644
index 00000000..2c9592f6
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/model/Note.java b/2022-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/model/Note.java
new file mode 100644
index 00000000..e8dea623
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/ConsoleIOService.java b/2022-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/ConsoleIOService.java
new file mode 100644
index 00000000..bf773074
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/NoteConverter.java b/2022-05/spring-00/architecture-origins/solution07_srp/src/main/java/ru/otus/services/NoteConverter.java
new file mode 100644
index 00000000..d8f8445b
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/.gitignore b/2022-05/spring-00/architecture-origins/solution08_srp/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution08_srp/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution08_srp/pom.xml b/2022-05/spring-00/architecture-origins/solution08_srp/pom.xml
new file mode 100644
index 00000000..f462ae91
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..1dd7742a
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java
new file mode 100644
index 00000000..5fa79742
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java
new file mode 100644
index 00000000..2c9592f6
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/model/Note.java b/2022-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/model/Note.java
new file mode 100644
index 00000000..10d3b8ee
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ApplicationRunner.java b/2022-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ApplicationRunner.java
new file mode 100644
index 00000000..21aa98b1
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ConsoleIOService.java b/2022-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/ConsoleIOService.java
new file mode 100644
index 00000000..bf773074
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/MenuCommandsProcessor.java b/2022-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/MenuCommandsProcessor.java
new file mode 100644
index 00000000..b9541d8b
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NoteConverter.java b/2022-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NoteConverter.java
new file mode 100644
index 00000000..d8f8445b
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NotesService.java b/2022-05/spring-00/architecture-origins/solution08_srp/src/main/java/ru/otus/services/NotesService.java
new file mode 100644
index 00000000..8d899f25
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/.gitignore b/2022-05/spring-00/architecture-origins/solution09_dip/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution09_dip/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution09_dip/pom.xml b/2022-05/spring-00/architecture-origins/solution09_dip/pom.xml
new file mode 100644
index 00000000..2e3b18f9
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..a27f1448
--- /dev/null
+++ b/2022-05/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, +ApplicationStopService, IOService стал Streams, методы MenuCommandsProcessor очистились от 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 menuCommandsProcessor = new MenuCommandsProcessorImpl(ioService, notesService,
+ noteConverter, applicationStopService);
+
+ new ApplicationRunner(ioService, applicationStopService, menuCommandsProcessor)
+ .run();
+ }
+}
diff --git a/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/config/AppSettings.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/config/AppSettings.java
new file mode 100644
index 00000000..b2344ece
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java
new file mode 100644
index 00000000..5fa79742
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java
new file mode 100644
index 00000000..2c9592f6
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/model/Note.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/model/Note.java
new file mode 100644
index 00000000..10d3b8ee
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationRunner.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationRunner.java
new file mode 100644
index 00000000..e10ac8c9
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopService.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopService.java
new file mode 100644
index 00000000..31d612f9
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java
new file mode 100644
index 00000000..4a99b272
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOService.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOService.java
new file mode 100644
index 00000000..5247912b
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOServiceStreams.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/IOServiceStreams.java
new file mode 100644
index 00000000..ca7dff21
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessor.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessor.java
new file mode 100644
index 00000000..3ce09c81
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessorImpl.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/MenuCommandsProcessorImpl.java
new file mode 100644
index 00000000..374e472e
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverter.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverter.java
new file mode 100644
index 00000000..7dfe1e57
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverterImpl.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NoteConverterImpl.java
new file mode 100644
index 00000000..1b351c82
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesService.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesService.java
new file mode 100644
index 00000000..019c6cd7
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesServiceImpl.java b/2022-05/spring-00/architecture-origins/solution09_dip/src/main/java/ru/otus/services/NotesServiceImpl.java
new file mode 100644
index 00000000..b8c15298
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/.gitignore b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/.gitignore
new file mode 100644
index 00000000..e62c33c2
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/.gitignore
@@ -0,0 +1,4 @@
+.idea/
+*.iml
+
+target/
diff --git a/2022-05/spring-00/architecture-origins/solution10_ocp_isp/pom.xml b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/pom.xml
new file mode 100644
index 00000000..3ce8c35e
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/Main.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/Main.java
new file mode 100644
index 00000000..46efea5d
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/Main.java
@@ -0,0 +1,46 @@
+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 showAllNotesMenuOption = new MenuOption(1, "Вывести все заметки");
+ var addNewNoteMenuOption = new MenuOption(2, "Добавить заметку");
+ var updateNoteMenuOption = new MenuOption(3, "Изменить заметку");
+ var deleteNoteMenuOption = new MenuOption(4, "Удалить заметку");
+ var stopApplicationMenuOption = new MenuOption(5, "Выйти");
+
+ var menuOptions = List.of(showAllNotesMenuOption, addNewNoteMenuOption,
+ updateNoteMenuOption, deleteNoteMenuOption, stopApplicationMenuOption
+ );
+ var menuOptionsRegistry = new MenuOptionsRegistryImpl(menuOptions);
+
+ var processors = List.of(
+ new ShowAllNotesSingleCommandProcessor(ioService, notesService, noteConverter, showAllNotesMenuOption),
+ new AddNewNoteSingleCommandProcessor(ioService, notesService, addNewNoteMenuOption),
+ new UpdateNoteSingleCommandProcessor(ioService, notesService, updateNoteMenuOption),
+ new DeleteNoteSingleCommandProcessor(ioService, notesService, deleteNoteMenuOption),
+ new StopApplicationSingleCommandProcessor(applicationStopService, stopApplicationMenuOption)
+ );
+
+ var menuCommandsProcessor = new MenuCommandsProcessorImpl(processors);
+
+ new ApplicationRunner(ioService, applicationStopService, menuOptionsRegistry, menuCommandsProcessor)
+ .run();
+ }
+}
diff --git a/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/AppSettings.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/AppSettings.java
new file mode 100644
index 00000000..15d18038
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/ApplicationStopServiceSettingsProvider.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/ApplicationStopServiceSettingsProvider.java
new file mode 100644
index 00000000..fe5d2ed7
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/DateTimeFormatProvider.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/config/DateTimeFormatProvider.java
new file mode 100644
index 00000000..9f864dc7
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuCommandProcessorNotFound.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuCommandProcessorNotFound.java
new file mode 100644
index 00000000..b9b89316
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/MenuItemIndexOutOfBoundsException.java
new file mode 100644
index 00000000..5fa79742
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/exceptions/NoteIndexOutOfBoundsException.java
new file mode 100644
index 00000000..2c9592f6
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/model/Note.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/model/Note.java
new file mode 100644
index 00000000..10d3b8ee
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationRunner.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationRunner.java
new file mode 100644
index 00000000..57a93b66
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopService.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopService.java
new file mode 100644
index 00000000..31d612f9
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/ApplicationStopServiceImpl.java
new file mode 100644
index 00000000..efd4d67c
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOService.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOService.java
new file mode 100644
index 00000000..5e8cd764
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOServiceStreams.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/IOServiceStreams.java
new file mode 100644
index 00000000..ca7dff21
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverter.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverter.java
new file mode 100644
index 00000000..7dfe1e57
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverterImpl.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NoteConverterImpl.java
new file mode 100644
index 00000000..5ff2c79f
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesService.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesService.java
new file mode 100644
index 00000000..019c6cd7
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesServiceImpl.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/NotesServiceImpl.java
new file mode 100644
index 00000000..b8c15298
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOption.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOption.java
new file mode 100644
index 00000000..2514200b
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistry.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistry.java
new file mode 100644
index 00000000..cd5c86cb
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistryImpl.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/menu/MenuOptionsRegistryImpl.java
new file mode 100644
index 00000000..30fca79d
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessor.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessor.java
new file mode 100644
index 00000000..7649900f
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/DeleteNoteSingleCommandProcessor.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/DeleteNoteSingleCommandProcessor.java
new file mode 100644
index 00000000..43898f61
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/InputService.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/InputService.java
new file mode 100644
index 00000000..52079551
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessor.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessor.java
new file mode 100644
index 00000000..68f7f77a
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessorImpl.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuCommandsProcessorImpl.java
new file mode 100644
index 00000000..495841cf
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuSingleCommandProcessor.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/MenuSingleCommandProcessor.java
new file mode 100644
index 00000000..fb5f7245
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/OutputService.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/OutputService.java
new file mode 100644
index 00000000..dd68ad88
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/ShowAllNotesSingleCommandProcessor.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/ShowAllNotesSingleCommandProcessor.java
new file mode 100644
index 00000000..8d902e69
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/StopApplicationSingleCommandProcessor.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/StopApplicationSingleCommandProcessor.java
new file mode 100644
index 00000000..4fbef62c
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/UpdateNoteSingleCommandProcessor.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/UpdateNoteSingleCommandProcessor.java
new file mode 100644
index 00000000..85116a15
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/utils/NotesListUtil.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/main/java/ru/otus/services/processors/utils/NotesListUtil.java
new file mode 100644
index 00000000..0f19e3cf
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/ApplicationStopServiceImplTest.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/ApplicationStopServiceImplTest.java
new file mode 100644
index 00000000..71ddf850
--- /dev/null
+++ b/2022-05/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-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/menu/MenuOptionsRegistryImplTest.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/menu/MenuOptionsRegistryImplTest.java
new file mode 100644
index 00000000..21c13cd5
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/menu/MenuOptionsRegistryImplTest.java
@@ -0,0 +1,44 @@
+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);
+ }
+
+ // Обратить внимание на работу с Optional
+ @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-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessorTest.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessorTest.java
new file mode 100644
index 00000000..d5f5b32d
--- /dev/null
+++ b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/AddNewNoteSingleCommandProcessorTest.java
@@ -0,0 +1,59 @@
+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-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/MenuCommandsProcessorImplTest.java b/2022-05/spring-00/architecture-origins/solution10_ocp_isp/src/test/java/ru/otus/services/processors/MenuCommandsProcessorImplTest.java
new file mode 100644
index 00000000..f18a7562
--- /dev/null
+++ b/2022-05/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
diff --git a/2022-05/spring-01/.gitignore b/2022-05/spring-01/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2022-05/spring-01/.gitignore
@@ -0,0 +1,24 @@
+target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
diff --git a/2022-05/spring-01/pom.xml b/2022-05/spring-01/pom.xml
new file mode 100644
index 00000000..6658aaa0
--- /dev/null
+++ b/2022-05/spring-01/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-01
+ 1.0
+
+ pom
+
+
+ spring-01-exercise
+ spring-01-solution
+
+
diff --git a/2022-05/spring-01/spring-01-exercise/pom.xml b/2022-05/spring-01/spring-01-exercise/pom.xml
new file mode 100644
index 00000000..e3aa2772
--- /dev/null
+++ b/2022-05/spring-01/spring-01-exercise/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-01-exercise
+ 1.0
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+
+
diff --git a/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/Main.java b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..e3201a16
--- /dev/null
+++ b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,17 @@
+package ru.otus.spring;
+
+//import org.springframework.context.support.ClassPathXmlApplicationContext;
+import ru.otus.spring.domain.Person;
+
+public class Main {
+
+ public static void main(String[] args) {
+ // TODO: создайте здесь класс контекста
+
+ // TODO: Получите Person Service
+
+ // Получите Person "Ivan"
+ Person ivan = null;
+ System.out.println("name: " + ivan.getName() + " age: " + ivan.getAge());
+ }
+}
diff --git a/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/dao/PersonDao.java b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..d33939bd
--- /dev/null
+++ b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonDao {
+
+ Person findByName(String name);
+}
diff --git a/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..7f7c97c6
--- /dev/null
+++ b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
@@ -0,0 +1,10 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public class PersonDaoSimple implements PersonDao {
+
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/domain/Person.java b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..c23be0c6
--- /dev/null
+++ b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,20 @@
+package ru.otus.spring.domain;
+
+public class Person {
+
+ private final String name;
+ private final int age;
+
+ public Person(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+}
diff --git a/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/service/PersonService.java b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/service/PersonService.java
new file mode 100644
index 00000000..9b83e7de
--- /dev/null
+++ b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/service/PersonService.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonService {
+
+ Person getByName(String name);
+}
diff --git a/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/service/PersonServiceImpl.java b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
new file mode 100644
index 00000000..794c29dd
--- /dev/null
+++ b/2022-05/spring-01/spring-01-exercise/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
@@ -0,0 +1,17 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonDao dao;
+
+ public PersonServiceImpl(PersonDao dao) {
+ this.dao = dao;
+ }
+
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2022-05/spring-01/spring-01-exercise/src/main/resources/spring-context.xml b/2022-05/spring-01/spring-01-exercise/src/main/resources/spring-context.xml
new file mode 100644
index 00000000..2cb9eabe
--- /dev/null
+++ b/2022-05/spring-01/spring-01-exercise/src/main/resources/spring-context.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/2022-05/spring-01/spring-01-solution/pom.xml b/2022-05/spring-01/spring-01-solution/pom.xml
new file mode 100644
index 00000000..124886be
--- /dev/null
+++ b/2022-05/spring-01/spring-01-solution/pom.xml
@@ -0,0 +1,26 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-01-solution
+ 1.0
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+
+ org.springframework
+ spring-context
+ 5.3.20
+
+
+
diff --git a/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/Main.java b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/Main.java
new file mode 100644
index 00000000..f5b2fafb
--- /dev/null
+++ b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/Main.java
@@ -0,0 +1,20 @@
+package ru.otus.spring;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import ru.otus.spring.domain.Person;
+import ru.otus.spring.service.PersonService;
+
+public class Main {
+
+ public static void main(String[] args) {
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
+ PersonService service = context.getBean(PersonService.class);
+ Person ivan = service.getByName("Ivan");
+ System.out.println("name: " + ivan.getName() + " age: " + ivan.getAge());
+
+ // Данная операция, в принципе не нужна.
+ // Мы не работаем пока что с БД, а Spring Boot сделает закрытие за нас
+ // Подробности - через пару занятий
+ context.close();
+ }
+}
diff --git a/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/dao/PersonDao.java b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/dao/PersonDao.java
new file mode 100644
index 00000000..d33939bd
--- /dev/null
+++ b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/dao/PersonDao.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonDao {
+
+ Person findByName(String name);
+}
diff --git a/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
new file mode 100644
index 00000000..7f7c97c6
--- /dev/null
+++ b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/dao/PersonDaoSimple.java
@@ -0,0 +1,10 @@
+package ru.otus.spring.dao;
+
+import ru.otus.spring.domain.Person;
+
+public class PersonDaoSimple implements PersonDao {
+
+ public Person findByName(String name) {
+ return new Person(name, 18);
+ }
+}
diff --git a/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/domain/Person.java b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/domain/Person.java
new file mode 100644
index 00000000..c23be0c6
--- /dev/null
+++ b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/domain/Person.java
@@ -0,0 +1,20 @@
+package ru.otus.spring.domain;
+
+public class Person {
+
+ private final String name;
+ private final int age;
+
+ public Person(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+}
diff --git a/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/service/PersonService.java b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/service/PersonService.java
new file mode 100644
index 00000000..9b83e7de
--- /dev/null
+++ b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/service/PersonService.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.domain.Person;
+
+public interface PersonService {
+
+ Person getByName(String name);
+}
diff --git a/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/service/PersonServiceImpl.java b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
new file mode 100644
index 00000000..794c29dd
--- /dev/null
+++ b/2022-05/spring-01/spring-01-solution/src/main/java/ru/otus/spring/service/PersonServiceImpl.java
@@ -0,0 +1,17 @@
+package ru.otus.spring.service;
+
+import ru.otus.spring.dao.PersonDao;
+import ru.otus.spring.domain.Person;
+
+public class PersonServiceImpl implements PersonService {
+
+ private final PersonDao dao;
+
+ public PersonServiceImpl(PersonDao dao) {
+ this.dao = dao;
+ }
+
+ public Person getByName(String name) {
+ return dao.findByName(name);
+ }
+}
diff --git a/2022-05/spring-01/spring-01-solution/src/main/resources/spring-context.xml b/2022-05/spring-01/spring-01-solution/src/main/resources/spring-context.xml
new file mode 100644
index 00000000..c3541cec
--- /dev/null
+++ b/2022-05/spring-01/spring-01-solution/src/main/resources/spring-context.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+