일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 완전탐색
- Baekjoon
- 구현
- Elasticsearch
- web
- 백준
- 누적합
- Generics
- binary search
- 코딩테스트
- parametric search
- Spring
- til
- programmers
- ES
- Algorithm
- 이분 탐색
- BFS
- 프로젝트
- CSS
- 이분탐색
- 계산기 만들기
- 프로그래머스
- OOP
- 알고리즘
- Java
- 브루트포스
- SpringBoot
- 객체지향
- 내일배움캠프
- Today
- Total
개발하는 햄팡이
[JAVA][계산기 만들기-4] Lv3. Enum을 활용한 계산기 만들기 본문
https://bitj-bitbox.tistory.com/40
[JAVA][계산기 만들기-1] github 라벨 편집, Issue Template 생성, PR Template 생성
저번 주에 HTML, CSS, JavaScript를 사용하여 간단한 팀소개 페이지 만들기가 끝나고이번 주 부터는 Java 기본 문법에 대해서 공부하면서 콘솔에 입출력을 받는 계산기 프로그램을 만드는 과제를 진행
bitj-bitbox.tistory.com
https://bitj-bitbox.tistory.com/41
[JAVA][계산기 만들기-2] Lv1. 클래스 사용하지 않고 기본 연산 계산기 만들기
오늘은 Lv.1 요구사항을 처리할 것이다.그런데 Lv1요구사항이 클래스를 사용하지 않고 기본 연산 계산기 만들기라는 것.클래스 사용하지 않고....라는게 무슨 말일까자바는 다 클래스인데....뭐 어
bitj-bitbox.tistory.com
https://bitj-bitbox.tistory.com/42
[JAVA][계산기 만들기-3] Lv2. 클래스를 사용하여 객체 지향 개념을 적용한 계산기 만들기
https://bitj-bitbox.tistory.com/40 [JAVA][계산기 만들기-1] github 라벨 편집, Issue Template 생성, PR Template 생성저번 주에 HTML, CSS, JavaScript를 사용하여 간단한 팀소개 페이지 만들기가 끝나고이번 주 부터는 Jav
bitj-bitbox.tistory.com
다시 요구사항 정리해서 이슈 만들고 작업 시작..!
아마 이 포스팅이 이번 프로젝트의 마지막 포스팅이 되지 않을까 싶다.
Enum, 제네릭, 람다 & 스트림 활용편을 한번에 올리려고 했더니 좀 길어질 것 같아서 그냥 나누기로 했다.
Lv3 부분은 필수 구현 부분이 아니지만 다 못하더라도 해당 기능을 구현하기 위해
개념에 대해 공부하고 고민해보는 과정이 의미있다고 생각하기 때문에 일단 도전!
도전하긴 하지만 나는 제네릭에 약하다...
enum을 활용한 계산기 ArithmeticCalculator
enum을 어떻게 활용할까 고민을 해봤는데
enum이란 일단 열거형 클래스이고, 상수들의 집합이다.
그리고 요구사항에서 이를 활용한 계산기를 만들어라 라고 했는데 그저 ADD('+') 이렇게 열거형 만들어서 입력받을때 enum으로 받고 끝은 아닐 것 같다.
그리고 예전에 현업 기업과 진행한 프로젝트에서 봤던 enum에 메소드 추가하기가 기억이 났다.
enum class안에 메소드를 정의하고 해당 메소드를 오버라이드하여 각 enum에 맞는 기능을 수행하도록 하는 것이었다.
계산기에서도 ADD던 SUBSTRACT던 모두 계산이라는 기능을 수행해야하기 때문에
enum값에 각각 메소드를 추가하는 방식으로 구현해도 좋을 것 같았다. (enum 전략 패턴 사용)
그래서 일단 OperatorType class를 만들고 해당 위치에 상수(add, substract, multiply, divide)를 열거했다.
그 다음 생성자도 만들었고 enum의 심볼을 가져오는 getter도 만들었다.
그리고 심볼에 맞는 enum값을 찾을 수 있는 메소드인 fromSymbol()도 만들기.
getSymbol은 enum값에서 가져오는 형태이므로 static으로 선언하지 않았지만
심볼로부터 enum값을 가져오는 것은 enum이 선언되지 않은 상태에서도(아직 어떤 enum상수값을 사용할지 모르는 상태에서도) 메소드를 불러올 수 있어야 하기 때문에 static으로 선언하였다.
그리고 이제 심볼별로 진행되는 계산 로직을 추가하는 것이다.
계산기의 모든 Operator는 연산을 해야하므로 메소드를 추상메소드로 선언하여 무조건 오버라이드 할 수 있게 하였다.
package calculator;
public enum OperatorType {
ADD('+') {
@Override
public double calculate(int a, int b) {
return a + b;
}
},
SUBTRACT('-') {
@Override
public double calculate(int a, int b) {
return a - b;
}
},
MULTIPLY('*') {
@Override
public double calculate(int a, int b) {
return a * b;
}
},
DIVIDE('/') {
@Override
public double calculate(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
};
private final char symbol;
OperatorType(char symbol) {
this.symbol = symbol;
}
public char getSymbol() {
return symbol;
}
public static OperatorType fromSymbol(char symbol) {
for (OperatorType op : values()) {
if (op.symbol == symbol) {
return op;
}
}
throw new IllegalArgumentException("알 수 없는 연산자입니다: " + symbol);
}
public abstract double calculate(int a, int b);
}
다음으로 enum을 사용하지 않은 계산기와 똑같이 동작하는 ArithmeticCalculator를 만들어보자.
컬렉션 만들어서 결과 저장하고 결과 삭제하는 로직 만들고..
calculate메소드를 만들면 되는데
여기서 enum 전략 패턴이 빛을 발한다.
지저분하던 switch문을 작성하지 않아도 되는 것...
근데 ArithmeticCalculator 메소드 이름도 calculate고
enum안에 만든 메소드 이름도 calculate라서 구분하기 쉽게 apply로 바꿔줬다.
엄청나게 깔끔해졌다.
이제 테스트를 돌려보는데 문제가 생겼다..
0으로 나눴을때 다시 입력하게 해야하는데 calculate메소드 부분에서 처리를 해줬더니
그냥 결과를 출력해버리고 있었다.
그래서 Exception처리를 메소드내에서 하지 않고 반복문을 돌고 있는 main에서 하는 방법으로 바꾸었다.
그렇게 되면 오류가 생겼을때 결과를 출력하지 않고 바로 반복문을 돌게 된다.
현재까지 최종 코드
App.java
package calculator;
import java.text.DecimalFormat;
import java.util.Scanner;
public class App {
private static final Scanner SCANNER = new Scanner(System.in);
private static final Calculator CALCULATOR = new Calculator();
private static final ArithmeticCalculator ARITHMETRIC_CALCULATOR = new ArithmeticCalculator();
private static final DecimalFormat df = new DecimalFormat();
public static void main(String[] args) {
System.out.println("Hello, App!");
while (true) {
System.out.println("계산을 중단하려면 exit 를 입력해주세요.");
int num1 = getPositiveInteger("첫 번째 숫자를 입력하세요. (exit입력 시 종료): ");
int num2 = getPositiveInteger("두 번째 숫자를 입력하세요. (exit입력 시 종료): ");
char operator = getOperator("사칙연산 기호를 입력하세요. (exit입력 시 종료): ");
try {
double result = CALCULATOR.calculate(num1, num2, operator);
double result2 = ARITHMETRIC_CALCULATOR.calculate(num1, num2, operator);
System.out.printf("[결과] %d %s %d = %s \n", num1, operator, num2, df.format(result));
System.out.printf("[결과] %d %s %d = %s \n", num1, operator, num2, df.format(result2));
} catch (ArithmeticException e) {
System.out.println("[Error] : " + e.getMessage());
} catch (Exception e) {
System.out.println("[Error] : 연산 중 오류가 발생했습니다. (" + e.getMessage() + ")");
}
}
}
private static int getPositiveInteger(String prompt) {
while (true) {
System.out.print(prompt);
String input = SCANNER.nextLine().trim();
handleExit(input);
try {
int num = Integer.parseInt(input);
if (num < 0) {
throw new IllegalArgumentException("[Error] : 양의 정수(0 포함)만 입력 가능합니다.");
}
return num;
} catch (NumberFormatException e) {
System.out.println("[Error] : 정수를 입력해주세요.");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
System.out.println("Unexpected Exception: " + e.getMessage());
}
}
}
private static char getOperator(String prompt) {
while (true) {
System.out.print(prompt);
String line = SCANNER.nextLine().trim();
handleExit(line);
if (line.length() == 1) {
char op = line.charAt(0);
if (op == '+' || op == '-' || op == '*' || op == '/') {
return op;
}
}
System.out.println("[Error] : 유효한 사칙연산 기호를 입력해주세요. (+, -, *, /)");
}
}
private static void handleExit(String input) {
if (input.equalsIgnoreCase("exit")) {
System.out.println("프로그램을 종료합니다.");
SCANNER.close();
System.exit(0);
}
}
}
Calculator.java
package calculator;
import java.util.LinkedList;
import java.util.List;
public class Calculator {
private final List<Double> results;
public double calculate(int num1, int num2, char operator) {
double result = 0;
switch (operator) {
case '+':
result = add(num1, num2);
break;
case '-':
result = subtract(num1, num2);
break;
case '*':
result = multiply(num1, num2);
break;
case '/':
result = divide(num1, num2);
break;
default:
System.out.println("[Error] : 알 수 없는 연산자입니다.");
}
this.results.add(result);
return result;
}
public Calculator() {
this.results = new LinkedList<>();
}
public List<Double> getResults() {
return this.results;
}
public void setResults(List<Double> newResults) {
this.results.clear();
if (newResults != null) {
this.results.addAll(newResults);
}
}
public void deleteFirstResult() {
this.results.remove(0);
}
private int add(int a, int b) {
return a + b;
}
private int subtract(int a, int b) {
return a - b;
}
private int multiply(int a, int b) {
return a * b;
}
private double divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
}
OperatorType.java
package calculator;
public enum OperatorType {
ADD('+') {
@Override
public double apply(int a, int b) {
return a + b;
}
},
SUBTRACT('-') {
@Override
public double apply(int a, int b) {
return a - b;
}
},
MULTIPLY('*') {
@Override
public double apply(int a, int b) {
return a * b;
}
},
DIVIDE('/') {
@Override
public double apply(int a, int b) {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
return (double) a / b;
}
};
private final char symbol;
OperatorType(char symbol) {
this.symbol = symbol;
}
public char getSymbol() {
return symbol;
}
public static OperatorType fromSymbol(char symbol) {
for (OperatorType op : values()) {
if (op.symbol == symbol) {
return op;
}
}
throw new IllegalArgumentException("알 수 없는 연산자입니다: " + symbol);
}
public abstract double apply(int a, int b);
}
ArithmeticCalculator.java
package calculator;
import java.util.LinkedList;
import java.util.List;
public class ArithmeticCalculator {
private final List<Double> results;
public ArithmeticCalculator() {
this.results = new LinkedList<>();
}
public List<Double> getResults() {
return results;
}
public void setResults(List<Double> newResults) {
this.results.clear();
if (newResults != null) {
this.results.addAll(newResults);
}
}
public double calculate(int num1, int num2, char operator) {
double result = OperatorType.fromSymbol(operator).apply(num1, num2);
this.results.add(result);
return result;
}
public void deleteFirstResult() {
this.results.remove(0);
}
}
'Back-End > Java' 카테고리의 다른 글
[JAVA][계산기 만들기-5] Lv3. Generics를 활용한 계산기 만들기 (2) | 2025.04.23 |
---|---|
[JAVA][계산기 만들기-3] Lv2. 클래스를 사용하여 객체 지향 개념을 적용한 계산기 만들기 (0) | 2025.04.22 |
[JAVA][계산기 만들기-1] github 라벨 편집, Issue Template 생성, PR Template 생성 (1) | 2025.04.18 |
[JAVA] OOP란? - 객체 지향 프로그래밍의 개념과 특징 (1) | 2025.04.16 |
[Java 기초] Java란? (4) | 2025.04.15 |