Приветствую всех, кто читает этот пост! Благодаря своей работе я часто сталкиваюсь с начинающими программистами и смотрю на их код. Иногда это красиво, иногда нет, но я понимаю, что человек учится, и чем дальше он осваивает лучшие практики и технологии, тем лучше становится его код. Но я все же хотел бы уделить некоторое внимание такой простой задаче, с которой сталкиваются практически все начинающие программисты, – написанию калькулятора
Вот что пишут начинающие программисты:
import java.util.Scanner; public class Calculator { public static void main(String[] args) { int sum = 0; int number1 = 0, number2 = 0; boolean correct = true; char operation1; Scanner sc = new Scanner(System.in); System.out.println("Enter first number:"); while (!sc.hasNextInt()) { System.out.println("Sorry, it is not a digit. Try again, please"); sc.next(); } number1 = sc.nextInt(); System.out.println("Thanks! You entered number: " + number1); System.out.println("Enter a number:"); while (!sc.hasNextInt()) { System.out.println("It is not a digit. Try again"); sc.next(); } number2 = sc.nextInt(); System.out.println("Thanks! It is a digit " + number2); do { correct = true; System.out.println("What would you like to do? + - / *"); operation1 = sc.next().charAt(0); switch (operation1) { case '+': sum = number1 + number2; System.out.println("Sum of this two equals to " + sum); break; case '-': sum = number1 - number2; System.out.println("The difference between equals to: " + sum); break; case '/': if (number2 != 0) { sum = number1 / number2; System.out.println("Divied equals to: " + sum); } else { System.out.println("Divided by zero is not allowed."); correct = false; } break; case '*': sum = number1 * number2; System.out.println("Multiplication of this two: " + sum); break; default: System.out.println("Unknow operation"); correct = false; break; } } while (correct != true); } }
Какие проблемы вы видите в этом коде? Я вижу следующие проблемы
- Все написано в основном методе и, безусловно, представляет собой более 10 строк
- Для этого кода нет тестов, то есть нет уверенности в том, что он будет работать правильно
- Структурный стиль программирования – оператор switch поощряет это
- С помощью сканера, но это можно простить, потому что у него есть удобные методы для получения номеров сразу.
Как бы вы исправили этот код? Я бы исправил данный код следующим образом
- Создал бы интерфейс операций для всех операций с числами
public interface Operation { int execute(int a, int b); }
И соответственно я бы реализовал этот интерфейс в следующих классах
public class Addition implements Operation { @Override public int execute(int a, int b) { return a + b; } } public class Deduction implements Operation { @Override public int execute(int a, int b) { return a - b; } } public class Division implements Operation { @Override public int execute(int a, int b) { return a / b; } } public class Multiplication implements Operation { @Override public int execute(int a, int b) { return a * b; } }
Я бы сделал то же самое с методами ввода и вывода, но прежде чем мы начнем их писать, давайте добавим очень полезную библиотеку – Apache Commons
org.apache.commons commons-lang3 3.11
Интерфейс для работы с вводом-выводом информации будет выглядеть следующим образом
public interface IO { int getNumber(String message, String errorMessage); Operation getOperation(String message); void printResult(String result); }
И вот как будет выглядеть его реализация
public class ConsoleIO implements IO { private final BufferedReader bufferedReader; private final ListallowedOperation; private Map operationMap; public ConsoleIO(final List allowedOperation) { this.bufferedReader = new BufferedReader(new InputStreamReader(System.in)); this.allowedOperation = allowedOperation; fullFillServiceMap(); } @Override public int getNumber(String message, String errorMessage) { if (StringUtils.isNotBlank(message)) { System.out.println(message); } boolean validNumber = false; int numberResult = 0; while (!validNumber) { try { String line = bufferedReader.readLine(); if (StringUtils.isNumeric(line)) { numberResult = Integer.parseInt(line); validNumber = true; } } catch (IOException e) { System.out.println(errorMessage); throw new RuntimeException(e); } } return numberResult; } @Override public Operation getOperation(String operationKey) { if (this.allowedOperation == null || this.allowedOperation.isEmpty()) { throw new IllegalStateException("Не заданы разрешенные операции"); } boolean validOperation = false; while (!validOperation) { if (this.allowedOperation.contains(operationKey)) { validOperation = true; } } return operationMap.get(operationKey); } @Override public void printResult(String result) { System.out.println(result); } private void fullFillServiceMap() { this.operationMap = new HashMap<>(); this.operationMap.put("+", new Addition()); this.operationMap.put("-", new Deduction()); this.operationMap.put("*", new Multiplication()); this.operationMap.put("/", new Division()); } }
Единственное, что осталось, – это написать сам калькулятор, то есть обработчик операций
public class Calculator { private IO io; private MapidOperationKeys; public Calculator(IO io) { this.io = io; fillMap(); } public void launch() { boolean working = true; while (working) { int number = io.getNumber("1.Addition\n2.Substraction\n3.Multiplication\n4.Divison\n5.Exit", "Not valid number"); if (number == 5) { working = false; } else { int result = 0; try { result = io.getOperation(idOperationKeys.get(number)) .execute( io.getNumber("Enter first number", "Not valid number"), io.getNumber("Enter second number", "Not valid number") ); io.printResult("The result of you operation equals to " + result); } catch (RuntimeException runtimeException) { io.printResult(runtimeException.getMessage()); } } } } private void fillMap() { idOperationKeys = new HashMap<>(); idOperationKeys.put(1, "+"); idOperationKeys.put(2, "-"); idOperationKeys.put(3, "*"); idOperationKeys.put(4, "/"); } }
Что ж, для того чтобы наша программа наконец заработала, мы отредактируем основной метод
public class App { public static void main(String[] args) { new Calculator( new ConsoleIO( Arrays.asList("+", "-", "*", "/") ) ).launch(); } }
Моя реализация не претендует на звание лучшего решения, однако я вижу в ней несколько преимуществ:
- Этот код проще для написания тестов
- Код открыт для добавления новых функций
- Никаких зависимостей от какой-либо реализации – мы используем абстракцию для ввода и вывода информации
Это все для меня, спасибо за ваше внимание!
Если вам понравилась статья, поделитесь с друзьями и поставьте лайк
Подписывайтесь и оставляйте свои комментарии
Оригинал: “https://dev.to/vrnsky/stop-writing-code-in-main-method-2bff”