2023-11 spring-02-arch added

This commit is contained in:
stvort
2024-02-07 17:38:33 +04:00
parent 87bd5591ce
commit 7072087038
134 changed files with 3752 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>architecture-origins</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<modules>
<module>solution01</module>
<module>solution02_codeface</module>
<module>solution03_kiss</module>
<module>solution04_kiss</module>
<module>solution05_dry</module>
<module>solution06_dry</module>
<module>solution07_srp</module>
<module>solution08_srp</module>
<module>solution09_dip</module>
<module>solution10_ocp_isp</module>
<module>solution10_ocp_isp_spring</module>
</modules>
</project>
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution01</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
@@ -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<String> 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("Ошибка при вводе числа");
}
}
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution02_codeface</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
@@ -0,0 +1,77 @@
package ru.otus;
import java.time.LocalDateTime;
import java.util.ArrayList;
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<String>();
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("Ошибка при вводе числа");
}
}
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution03_kiss</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
@@ -0,0 +1,102 @@
package ru.otus;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.stream.IntStream;
// Вынос печати меню, чтения пункта меню и выполнения команд в методы.
// - Убрали один уровень вложенности
// - Отделили что делает приложение от того, как оно это делает
public class Main {
private static final int MENU_OPTION_SHOW_ALL_NOTES = 1;
private static final int MENU_OPTION_ADD_NEW_NOTE = 2;
private static final int MENU_OPTION_UPDATE_NOTE = 3;
private static final int MENU_OPTION_DELETE_NOTE = 4;
private static final int MENU_OPTION_EXIT = 5;
public static void main(String[] args) {
var notes = new ArrayList<String>();
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<String> 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<String> notes) {
System.out.println("Введите текст заметки...");
var noteText = userInput.nextLine();
var finalNoteText = LocalDateTime.now() + " | " + noteText;
notes.add(finalNoteText);
}
private static void updateNote(Scanner userInput, List<String> 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<String> notes) {
System.out.println("Введите номер удаляемой заметки...");
var deletedNoteNumber = Integer.parseInt(userInput.nextLine());
if (deletedNoteNumber <= 0 || deletedNoteNumber > notes.size()) {
System.out.println("Введен несуществующий номер заметки");
return;
}
notes.remove(deletedNoteNumber - 1);
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution04_kiss</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
@@ -0,0 +1,106 @@
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<String>();
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<String> 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<String> 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<String> notes) {
System.out.println("Введите текст заметки...");
var noteText = userInput.nextLine();
var finalNoteText = LocalDateTime.now() + " | " + noteText;
notes.add(finalNoteText);
}
private static void updateNote(Scanner userInput, List<String> 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<String> notes) {
System.out.println("Введите номер удаляемой заметки...");
var deletedNoteNumber = Integer.parseInt(userInput.nextLine());
if (deletedNoteNumber <= 0 || deletedNoteNumber > notes.size()) {
System.out.println("Введен несуществующий номер заметки");
return;
}
notes.remove(deletedNoteNumber - 1);
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution05_dry</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
@@ -0,0 +1,117 @@
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<String>();
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<String> 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<String> 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<String> notes) {
var noteText = readStringWithPrompt(userInput, "Введите текст заметки...");
var finalNoteText = LocalDateTime.now() + " | " + noteText;
notes.add(finalNoteText);
}
private static void updateNote(Scanner userInput, List<String> 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<String> 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();
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution06_dry</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
@@ -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<String>();
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<String> 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<String> 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<String> notes) {
var noteText = readStringWithPrompt(userInput, "Введите текст заметки...");
var finalNoteText = LocalDateTime.now() + " | " + noteText;
notes.add(finalNoteText);
}
private static void checkNoteNumber(int noteNumber, List<String> 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<String> 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<String> 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();
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException {
public MenuItemIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException {
public NoteIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution07_srp</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
@@ -0,0 +1,108 @@
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<Note>();
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<Note> 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<Note> 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<Note> notes) {
var noteText = ioService.readStringWithPrompt("Введите текст заметки...");
notes.add( Note.of(noteText));
}
private static void checkNoteNumber(int noteNumber, List<Note> 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<Note> 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<Note> notes) {
var deletedNoteNumber = ioService.readIntWithPrompt("Введите номер удаляемой заметки...");
checkNoteNumber(deletedNoteNumber, notes);
notes.remove(deletedNoteNumber - 1);
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException {
public MenuItemIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException {
public NoteIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -0,0 +1,25 @@
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;
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution08_srp</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
@@ -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();
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException {
public MenuItemIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException {
public NoteIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -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);
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -0,0 +1,57 @@
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");
}
}
}
@@ -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();
}
}
@@ -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<String, Note> notes;
public NotesService() {
notes = new HashMap<>();
}
public List<Note> 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);
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution09_dip</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
@@ -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();
}
}
@@ -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;
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException {
public MenuItemIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException {
public NoteIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -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);
}
}
@@ -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();
}
}
@@ -0,0 +1,6 @@
package ru.otus.services;
public interface ApplicationStopService {
boolean isApplicationRunning();
void stopApplication();
}
@@ -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);
}
}
@@ -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);
}
@@ -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();
}
}
@@ -0,0 +1,13 @@
package ru.otus.services;
public interface MenuCommandsProcessor {
void showAllNotes();
void addNewNote();
void updateNote();
void deleteNote();
void stopApplication();
}
@@ -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");
}
}
}
@@ -0,0 +1,7 @@
package ru.otus.services;
import ru.otus.model.Note;
public interface NoteConverter {
String convertNoteToString(int noteNumber, Note note);
}
@@ -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();
}
}
@@ -0,0 +1,13 @@
package ru.otus.services;
import ru.otus.model.Note;
import java.util.List;
public interface NotesService {
List<Note> getAll();
void save(Note note);
void remove(String id);
}
@@ -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<String, Note> notes;
public NotesServiceImpl() {
notes = new HashMap<>();
}
@Override
public List<Note> 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);
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution10_ocp_isp</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.jupiter.version>5.10.1</junit.jupiter.version>
<mockito.version>5.8.0</mockito.version>
<assertj.version>3.24.2</assertj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- AssetrtJ -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -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();
}
}
@@ -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;
}
}
@@ -0,0 +1,5 @@
package ru.otus.config;
public interface ApplicationStopServiceSettingsProvider {
boolean isConfirmExit();
}
@@ -0,0 +1,5 @@
package ru.otus.config;
public interface DateTimeFormatProvider {
String getDateTimeFormat();
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class MenuCommandProcessorNotFound extends RuntimeException {
public MenuCommandProcessorNotFound(String message) {
super(message);
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class MenuItemIndexOutOfBoundsException extends IndexOutOfBoundsException {
public MenuItemIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -0,0 +1,7 @@
package ru.otus.exceptions;
public class NoteIndexOutOfBoundsException extends IndexOutOfBoundsException {
public NoteIndexOutOfBoundsException(String s) {
super(s);
}
}
@@ -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);
}
}
@@ -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();
}
}
@@ -0,0 +1,6 @@
package ru.otus.services;
public interface ApplicationStopService {
boolean isApplicationRunning();
void stopApplication();
}
@@ -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);
}
}
@@ -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 {
}
@@ -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();
}
}
@@ -0,0 +1,7 @@
package ru.otus.services;
import ru.otus.model.Note;
public interface NoteConverter {
String convertNoteToString(int noteNumber, Note note);
}
@@ -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();
}
}
@@ -0,0 +1,13 @@
package ru.otus.services;
import ru.otus.model.Note;
import java.util.List;
public interface NotesService {
List<Note> getAll();
void save(Note note);
void remove(String id);
}
@@ -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<String, Note> notes;
public NotesServiceImpl() {
notes = new HashMap<>();
}
@Override
public List<Note> 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);
}
}
@@ -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;
}
}
@@ -0,0 +1,9 @@
package ru.otus.services.menu;
import java.util.List;
import java.util.Optional;
public interface MenuOptionsRegistry {
List<MenuOption> getAvailableMenuOptions();
Optional<MenuOption> getMenuOptionById(int id);
}
@@ -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<Integer, MenuOption> options;
public MenuOptionsRegistryImpl(List<MenuOption> options) {
this.options = options.stream()
.collect(Collectors.toUnmodifiableMap(MenuOption::getId, Function.identity()));
}
@Override
public List<MenuOption> getAvailableMenuOptions() {
return options.values().stream().collect(Collectors.toUnmodifiableList());
}
@Override
public Optional<MenuOption> getMenuOptionById(int id) {
return Optional.ofNullable(options.get(id));
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -0,0 +1,9 @@
package ru.otus.services.processors;
public interface InputService {
int readInt();
int readIntWithPrompt(String prompt);
String readStringWithPrompt(String prompt);
}
@@ -0,0 +1,7 @@
package ru.otus.services.processors;
import ru.otus.services.menu.MenuOption;
public interface MenuCommandsProcessor {
void processMenuCommand(MenuOption selectedMenuOption);
}
@@ -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<MenuOption, MenuSingleCommandProcessor> processors;
public MenuCommandsProcessorImpl(List<MenuSingleCommandProcessor> 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();
}
}
@@ -0,0 +1,8 @@
package ru.otus.services.processors;
import ru.otus.services.menu.MenuOption;
public interface MenuSingleCommandProcessor {
void processCommand();
MenuOption getProcessedCommandOption();
}
@@ -0,0 +1,5 @@
package ru.otus.services.processors;
public interface OutputService {
void outputString(String s);
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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");
}
}
}
@@ -0,0 +1,37 @@
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.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@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());
}
}
@@ -0,0 +1,43 @@
package ru.otus.services.menu;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("Реестр опций меню ")
class MenuOptionsRegistryImplTest {
private List<MenuOption> 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);
}
}
@@ -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);
}
}
@@ -0,0 +1,57 @@
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.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@DisplayName("Процессор команд меню ")
class MenuCommandsProcessorImplTest {
private List<MenuSingleCommandProcessor> singleCommandProcessors;
private MenuCommandsProcessorImpl processor;
@BeforeEach
void setUp() {
singleCommandProcessors = prepareProcessorsMocks();
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);
}
private static List<MenuSingleCommandProcessor> prepareProcessorsMocks() {
List<MenuSingleCommandProcessor> processors = 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);
processors.add(singleCommandProcessor);
}
return processors;
}
}
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>solution10_ocp_isp_spring</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.jupiter.version>5.10.1</junit.jupiter.version>
<mockito.version>5.8.0</mockito.version>
<assertj.version>3.24.2</assertj.version>
<spring.version>6.1.3</spring.version>
<checkstyle-plugin.version>3.2.2</checkstyle-plugin.version>
<checkstyle.version>10.11.0</checkstyle.version>
<checkstyle.config.url>https://raw.githubusercontent.com/OtusTeam/Spring/master/checkstyle.xml</checkstyle.config.url>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- AssetrtJ -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${checkstyle-plugin.version}</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>${checkstyle.version}</version>
</dependency>
</dependencies>
<configuration>
<configLocation>${checkstyle.config.url}</configLocation>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,15 @@
package ru.otus;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ru.otus.services.ApplicationRunner;
// Spring
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/spring-context.xml");
ApplicationRunner runner = ctx.getBean(ApplicationRunner.class);
runner.run();
}
}
@@ -0,0 +1,22 @@
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;
}
}

Some files were not shown because too many files have changed in this diff Show More