Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,15 @@
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не очень понял зачем нужна эта зависимость, Тестов вроде бы нет

<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
7 changes: 7 additions & 0 deletions src/main/java/com/javarush/bakhtin/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.javarush.bakhtin;

public class Application {
public static void main(String[] args) {
new Console().run();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

в этой точке разумнее было бы собрать все приложениесо всеми его компонентами, тогда не пришлось бы их рождать внутри методов, и его структура была бы более прозрачной

}
}
34 changes: 34 additions & 0 deletions src/main/java/com/javarush/bakhtin/Caesar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.javarush.bakhtin;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;

public class Caesar {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

У этого класса нет никакого изменяемого внутреннего состояния, поэтому достаточно родить его один раз и просто обращаться к методам этого экземпляра


private final int FIRST_UNICODE_LETTER = 9;
private final int LAST_UNICODE_LETTER = 1104;
private final int NUM_OF_UNICODE_LETTERS = LAST_UNICODE_LETTER - FIRST_UNICODE_LETTER;

public void decode(int key, Path from, Path to) throws IOException {
encode(-key, from, to);
}

public void encode(int key, Path from, Path to) throws IOException {
try (FileReader reader = FileUtil.toFileReader(from);
FileWriter writer = FileUtil.toFileWriter(to)) {
while (reader.ready()) {
int inputUnicodeCode = reader.read();
char resultChar = encodeChar(key, inputUnicodeCode);
writer.write(resultChar);
}
}
}

protected char encodeChar(int key, int unicodeCode) {
return (char) ((unicodeCode - FIRST_UNICODE_LETTER + key % NUM_OF_UNICODE_LETTERS + NUM_OF_UNICODE_LETTERS)
% NUM_OF_UNICODE_LETTERS + FIRST_UNICODE_LETTER); // Общая формула для циклического сдвига
}

}
22 changes: 22 additions & 0 deletions src/main/java/com/javarush/bakhtin/Console.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.javarush.bakhtin;

import java.util.InputMismatchException;

public class Console {

private final MenuController menuController = new MenuController();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вот то о чем я говорил, все компоненты лучше создать в одной общей точке а потом на основе сконструировать приложение, передать эти компоненты в него можно через конструктор например


protected void run() {
while (true) {
menuController.printCommands();
try {
int answer = menuController.getUserCommand();
menuController.executeCommand(answer);
} catch (InputMismatchException e) {
System.err.println("Ошибка, введите число");
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/javarush/bakhtin/FileUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.javarush.bakhtin;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class FileUtil {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

очевидно это вспомогательный класс, утилитный, в таких всегда делают приватный конструктор


public static FileWriter toFileWriter(Path toPath) throws IOException {
try {
if (!Files.exists(toPath)) {
Files.createFile(toPath);
}
return new FileWriter(toPath.toFile());
} catch (FileNotFoundException e) {
throw new RuntimeException("Ошибка, файл не найден");
}
}

public static FileReader toFileReader(Path fromPath){
try {
return new FileReader(fromPath.toFile());
} catch (FileNotFoundException e) {
throw new RuntimeException("Ошибка, файл не найден");
}
}

}
43 changes: 43 additions & 0 deletions src/main/java/com/javarush/bakhtin/MenuController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.javarush.bakhtin;

import com.javarush.bakhtin.command.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Scanner;

public class MenuController {

private static final Scanner consoleInput = new Scanner(System.in);

private static final String MENU_MESSAGE = "Введите цифру, соответствующую команде:";
private static final HashMap<Integer, MenuCommand> menuItemMap = new HashMap<>(3);

protected MenuController() {
menuItemMap.put(1, new EncoderCommand());
menuItemMap.put(2, new DecoderCommand());
menuItemMap.put(3, new BruteForce());
menuItemMap.put(4, new ExitCommand());
}

protected void executeCommand(int answer) throws IOException {
menuItemMap.get(answer).execute();
}

protected void printCommands() {
System.out.println(MENU_MESSAGE);
for (var menuItem : MenuController.menuItemMap.entrySet()) {
System.out.print(menuItem.getValue().getCommandName());
System.out.print(" - ");
System.out.println(menuItem.getKey());
}
}

public int getUserCommand() {
int answer = Integer.parseInt(consoleInput.nextLine());
if (answer < 1 || answer > 4) {
throw new RuntimeException("Ошибка, введите число от 1 до 4");
}
return answer;
}
}
72 changes: 72 additions & 0 deletions src/main/java/com/javarush/bakhtin/command/BruteForce.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.javarush.bakhtin.command;

import com.javarush.bakhtin.Caesar;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BruteForce implements MenuCommand {

private static final String COMMAND_NAME = "Брутфорс";
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Константы невозможно изменить поэтому они почти всегда статические и почти всегда публичные, во-первых тогда не нужен getter А во-вторых тогда все строковые константы можно разместить в удобные классы например в интерфейсы, так будет проще сделать предложение многоязычным в случае необходимости


public String getCommandName() {
return COMMAND_NAME;
}

private static final Pattern WORD_PATTERN = Pattern.compile("[а-я]{4,20}");

private Set<String> wordSet = new HashSet<>();
private Set<String> encodedWordSet = new HashSet<>();

private final CaesarParamReader caesarParamReader = new CaesarParamReader();
private final Caesar caesar = new Caesar();

public void makeAWordListFromDictionary() throws IOException {
String dict = Files.readString(caesarParamReader.getDictFromUser());
Matcher matcher = WORD_PATTERN.matcher(dict);
while (matcher.find()) {
wordSet.add(matcher.group());
}
}

private void makeAWordListFromDecodedFile(Path path) throws IOException {
encodedWordSet.clear();
String text = Files.readString(path);
Matcher matcher = WORD_PATTERN.matcher(text);
while (matcher.find()) {
encodedWordSet.add(matcher.group());
}
}

public void execute() throws IOException {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

чем важнее метод тем выше он находится в теле класса, поэтому обычно сначала идут публичные а в конце идут приватные

makeAWordListFromDictionary();
int maxNumOfMatches = 0;
int shiftOfMaxNumOfMatches = 0;
Path encodedPath = caesarParamReader.getEncodedFromUser();
Path decodedPath = caesarParamReader.getDecodedFromUser();

for (int shift = -50; shift <= 50; shift++) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

почему 50 а не 55 например, когда в коде появляются такие константы их называют магическими из-за того, что невозможно определить почему они именно такие и на что повлияет их изменение

System.out.println("Старт " + shift);
caesar.decode(shift, encodedPath, decodedPath);
makeAWordListFromDecodedFile(decodedPath);
int findCounter = 0;
for (String string : encodedWordSet) {
if (wordSet.contains(string)) {
findCounter++;
}
}
System.out.println("Стоп: найдено слов:" + findCounter);
if (findCounter > maxNumOfMatches) {
maxNumOfMatches = findCounter;
shiftOfMaxNumOfMatches = shift;
}
}
System.out.println("Правильный ключ: " + shiftOfMaxNumOfMatches);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

плохо что вывод находится непосредственно внутри вычислительной функции, обычно эти вещи делают отдельными, это позволяет легко заменить реализацию выводы без изменения логики предложения, и наоборот

caesar.decode(shiftOfMaxNumOfMatches, encodedPath, decodedPath);
System.out.println("Брутфорс завершен");
}
}
68 changes: 68 additions & 0 deletions src/main/java/com/javarush/bakhtin/command/CaesarParamReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.javarush.bakhtin.command;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;

public class CaesarParamReader {

private static final String KEY_INPUT_MESSAGE = "Введите ключ шифрования, по умолчанию - 1:";
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вот тут почти как надо. Но надо было сделать все константы публичными и выделить для них отдельный класс

private static final String FROM_PATH_INPUT_MESSAGE = "Введите путь, откуда вы хотите считать данные или нажмите enter, чтобы выбрать ";
private static final String DICT_PATH_INPUT_MESSAGE = "Введите путь к словарю или нажмите enter, чтобы выбрать ";
private static final String ENCODED_PATH_INPUT_MESSAGE = "Введите путь к зашифрованному файлу или нажмите enter, чтобы выбрать ";
private static final String DECODED_PATH_INPUT_MESSAGE = "Введите путь к расшифрованному файлу или нажмите enter, чтобы выбрать ";
private static final Path DEFAULT_DICT_PATH = Path.of("text", "dict.txt");
private static final Path DEFAULT_ENCODED_PATH = Path.of("text", "encrypted.txt");
private static final Path DEFAULT_DECODED_PATH = Paths.get("text", "decrypted.txt");

private final Scanner consoleScanner = new Scanner(System.in);

private static int key = 1;
private static final CaesarParamReader instance = new CaesarParamReader();

public static CaesarParamReader getInstance() {
return instance;
}

private Path getPath(Path defaultPath) {
String consoleString = consoleScanner.nextLine();
if (!consoleString.isEmpty()) {
return Path.of(consoleString);
}
return defaultPath;
}

protected int getKeyFromUser() {
System.out.print(KEY_INPUT_MESSAGE);
try {
int inputKey = Integer.parseInt(consoleScanner.nextLine());
if (inputKey != 0) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Когда метод конкретного экземпляра класса изменяет некоторое статическая поле, для этого должны быть очень веские причины. То есть если в этом поле хранится что-то такое что одинаково важно для всех экземплярах этого класса. Здесь это не так

key = inputKey;
}
return key;
} catch (NumberFormatException e) {
throw new RuntimeException("Ошибка, введите число");
}
}

protected Path getFromPathFromUser(Path defaultFrom) {
System.out.println(FROM_PATH_INPUT_MESSAGE + defaultFrom.getFileName());
return getPath(defaultFrom);
}

protected Path getDictFromUser() {
System.out.println(DICT_PATH_INPUT_MESSAGE + DEFAULT_DICT_PATH.getFileName());
return getPath(DEFAULT_DICT_PATH);
}

protected Path getEncodedFromUser() {
System.out.println(ENCODED_PATH_INPUT_MESSAGE + DEFAULT_ENCODED_PATH.getFileName());
return getPath(DEFAULT_ENCODED_PATH);
}

protected Path getDecodedFromUser() {
System.out.println(DECODED_PATH_INPUT_MESSAGE + DEFAULT_DECODED_PATH.getFileName());
return getPath(DEFAULT_DECODED_PATH);
}

}
24 changes: 24 additions & 0 deletions src/main/java/com/javarush/bakhtin/command/DecoderCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.javarush.bakhtin.command;

import com.javarush.bakhtin.Caesar;

import java.io.IOException;
import java.nio.file.Path;

public class DecoderCommand implements MenuCommand {

private static final String COMMAND_NAME = "Расшифровать";

public String getCommandName() {
return COMMAND_NAME;
}

public void execute() throws IOException {
var reader = CaesarParamReader.getInstance();
int key = reader.getKeyFromUser();
Path from = reader.getEncodedFromUser();
Path to = reader.getDecodedFromUser();
new Caesar().decode(key, from, to);
}

}
27 changes: 27 additions & 0 deletions src/main/java/com/javarush/bakhtin/command/EncoderCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.javarush.bakhtin.command;

import com.javarush.bakhtin.Caesar;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class EncoderCommand implements MenuCommand {

private static final Path DEFAULT_TEXT_PATH = Paths.get("text", "text.txt");
private static final String COMMAND_NAME = "Зашифровать";

public String getCommandName() {
return COMMAND_NAME;
}

public void execute() throws IOException {
var reader = CaesarParamReader.getInstance();
int key = reader.getKeyFromUser();
Path from = reader.getFromPathFromUser(DEFAULT_TEXT_PATH);
Path to = reader.getEncodedFromUser();

new Caesar().encode(key, from, to);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

интересно а зачем при каждом вызове этого метода заново рождается экземпляр вычислительного класса?

}

}
15 changes: 15 additions & 0 deletions src/main/java/com/javarush/bakhtin/command/ExitCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.javarush.bakhtin.command;

public class ExitCommand implements MenuCommand {

private static final String COMMAND_NAME = "Выход из программы";

public String getCommandName() {
return COMMAND_NAME;
}

public void execute() {
System.out.print("Выход из программы...");
System.exit(0);
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/javarush/bakhtin/command/MenuCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.javarush.bakhtin.command;

import java.io.IOException;

public interface MenuCommand {

String getCommandName();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

То что есть интерфейс это хорошо


void execute() throws IOException;
}