이 프로젝트는 자바로 구현된 계산기 애플리케이션으로, 단계별로 기능이 향상되는 세 개의 레벨로 구성되어 있습니다. 객체지향 프로그래밍의 원칙을 적용하고, 제네릭, 열거형, 람다 및 스트림 API와 같은 자바의 다양한 기능을 활용합니다.
src/
└── com/
└── example/
└── calculator/
├── level1/
│ └── App.java
├── level2/
│ ├── App.java
│ └── Calculator.java
└── level3/
├── App.java
├── ArithmeticCalculator.java
└── OperatorType.java
- 사용자로부터 두 개의 정수와 사칙연산 기호를 입력받아 계산을 수행합니다.
- 기본적인 예외 처리가 구현되어 있습니다.
- 결과값은 저장되지 않습니다.
- Level 1의 기능을 향상시켜 별도의 Calculator 클래스로 로직을 분리했습니다.
- 계산 결과를 ArrayList에 저장하고 조회할 수 있습니다.
- 최대 5개의 결과만 저장되도록 구현되어 있습니다.
- 제네릭을 활용하여 다양한 숫자 타입(Integer, Double 등)을 지원합니다.
- Enum을 사용하여 연산자 타입을 관리합니다.
- 람다식과 스트림 API를 활용한 고급 결과 필터링 기능이 구현되어 있습니다.
- 소수점이 있는 결과와 정수 결과를 적절히 처리합니다.
- 덧셈(+), 뺄셈(-), 곱셈(*), 나눗셈(/)의 사칙연산을 지원합니다.
- 나눗셈 시 0으로 나누는 경우에 대한 예외 처리가 구현되어 있습니다.
- 계산 결과를 저장하고 최근 5개까지만 유지합니다.
- 저장된 결과 중에서 특정 값보다 큰 결과만 필터링하여 조회할 수 있습니다.
getGreaterResults(double threshold)메서드는 람다식과 스트림 API를 활용하여 구현되었습니다.- 이 메서드는 저장된 결과 중에서 입력받은 기준값보다 큰 결과들만 반환합니다.
- 프로그램을 실행합니다.
- 첫 번째 숫자를 입력합니다.
- 두 번째 숫자를 입력합니다.
- 사칙연산 기호(+, -, *, /)를 입력합니다.
- 계산 결과가 표시되고 저장됩니다.
- 특정 값보다 큰 결과를 조회하려면 'y'를 입력하고 기준값을 입력합니다.
- 계속 계산하려면 아무 키나 누르고, 종료하려면 'exit'를 입력합니다.
현재 구현된 람다와 스트림 기능 외에도 다음과 같은 기능을 추가로 구현해 볼 수 있습니다:
- 결과 중 최댓값/최솟값 찾기
- 결과의 평균값 계산하기
- 특정 범위 내의 결과만 필터링하기
- 결과를 오름차순/내림차순으로 정렬하기
- 짝수/홀수 결과만 필터링하기
-
문제: 사용자가 숫자가 아닌 값을 입력했을 때 프로그램이 비정상 종료됩니다.
- 해결:
InputMismatchException을 캐치하여 적절한 오류 메시지를 표시하고 계속 실행되도록 처리했습니다. - 코드:
try-catch블록으로sc.nextDouble()또는sc.nextInt()메서드를 감싸서 예외를 처리합니다.
- 해결:
-
문제: 입력 버퍼에 남아있는 개행 문자로 인해 다음 입력이 무시되는 현상이 발생합니다.
- 해결: 숫자 입력 후 항상
sc.nextLine()을 호출하여 버퍼를 비웁니다. - 코드: 예를 들어,
num1 = sc.nextDouble(); sc.nextLine();와 같이 처리합니다.
- 해결: 숫자 입력 후 항상
-
문제: 나눗셈 연산 시 0으로 나누는 경우 프로그램이 비정상 종료됩니다.
- 해결: 0으로 나누는 경우를 사전에 체크하여 예외를 발생시키고, 이를 메인 프로그램에서 캐치하여 처리합니다.
- 코드:
if (b == 0) throw new ArithmeticException("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
-
문제: 연산 결과가 정수형일 경우와 소수점이 있는 경우를 구분하여 처리해야 합니다.
- 해결: 결과값이 정수인지 확인하여 적절한 타입으로 변환하여 저장합니다.
- 코드:
if (result == (int)result) { return (int)result; } else { return result; }
- 문제: Level 1에서는 모든 로직이 하나의 클래스에 있어 유지보수가 어렵습니다.
- 해결: Level 2에서는 계산 로직을 별도의 Calculator 클래스로 분리하여 관심사 분리 원칙을 적용했습니다.
- 개선: Level 3에서는 Enum을 사용하여 연산자 타입을 관리하고, 제네릭을 활용하여 더 유연한 설계를 구현했습니다.
- 문제: 람다와 스트림을 처음 사용할 때 구문이 익숙하지 않아 오류가 발생할 수 있습니다.
- 해결: 간단한 필터링 작업부터 시작하여 점진적으로 복잡한 스트림 연산을 적용합니다.
- 예시:
results.stream().filter(result -> result.doubleValue() > threshold).collect(Collectors.toList());