핵심목표 : 자바 문법의 실습과 객체 지향 개념의 적용
이 버전에서는 자바의 객체 지향 원칙을 적용하여 클래스를 정의하고, 연산을 수행하는 메소드를 작성해야 합니다. 이 과정에서 클래스와 객체의 개념을 이해하고 활용하는 방법을 익힐 수 있습니다.
이전에 만든 1단계 계산기를 활용하고 싶어서 방법을 구상해보고 추가적으로 필요한 부분은 구글링하면서 시간이 후루룩 약 3시간 정도 걸린 것 같다.
첫 클래스를 3시 쯤에 생성하고 예시코드와 1단계 계산기를 보며 어떻게 분할하면 좋을지 한참을 고민하다가
얼추 마무리하기 시작한 시간이 대략 5시 50분 정도였다.
이번 2단계 계산기는 85%가 구글링
인 것 같아서 이걸 내가 짠 코드라고 부를 수 있을까? 에 대해 생각이 많아지는 계기가 되었다. 다음 번에는 문법을 더 공부해서 A to Z
의 모든 과정을 직접 입력하고 싶다.
❗ 필수 기능 가이드
- 사칙연산을 수행 후, 결과값 반환 메서드 구현 & 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
- 사칙연산을 수행한 후, 결과값을 반환하는 메서드 구현
- 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
- 1) 양의 정수 2개(0 포함)와 연산 기호를 매개변수로 받아 사칙연산(➕,➖,✖️,➗) 기능을 수행한 후
2) 결과 값을 반환하는 메서드와 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성합니다.
1단계 계산기 때 객체지향이 되기 위한 피드백을 여러 사람에게 받아보았는데 은닉화와 캡슐화를 중요하게 다루는 것을 보고 지금의 내가 할 수 있는 선에서 최대한 은닉 + 캡슐화
에 대해 신경을 쓰고자 클래스 분할은 크게 Calculator
, Main
, GuideProcessing
으로 나눠보았다.
Calculator
: 계산 과정이 들어있는 곳
GuideProcessing
: 흐름제어를 위한 모든 과정이 있는 곳
Main
: 프로그램을 실행만 하는 곳
// 사용자가 입력한 기호에 따른 case(operator) 실행
public double calculate(int operandFirst, int operandSecond, String operator) throws InputMismatchException, ArithmeticException, OperatorInputException {
switch (operator) {
// 덧셈
case "+":
result = operandFirst + operandSecond;
saveCalculationProcess(operandFirst, operandSecond, result, operator);
break;
// 뺄셈
case "-":
result = operandFirst - operandSecond;
saveCalculationProcess(operandFirst, operandSecond, result, operator);
break;
// 곱셈
case "*":
result = operandFirst * operandSecond;
saveCalculationProcess(operandFirst, operandSecond, result, operator);
break;
// 나눗셈
case "/":
if (operandSecond == 0) {
throw new ArithmeticException("0으로는 나눌 수 없습니다." + System.lineSeparator() + "==================== \uD83D\uDEA8 다시 시작 합니다! ====================");
}
result = operandFirst / operandSecond;
saveCalculationProcess(operandFirst, operandSecond, result, operator);
break;
// 나머지
case "%":
result = operandFirst % operandSecond;
saveCalculationProcess(operandFirst, operandSecond, result, operator);
break;
// 지정 연산자 외 문자 입력 시 오류 메세지 출력
default:
throw new OperatorInputException("+, -, *, /, % 연산자만 입력해주세요" + System.lineSeparator() + "==================== \uD83D\uDEA8 다시 시작 합니다! ====================");
}
return result;
}
1단계 계산기와 동일하게 사용하면서 3주차와 4주차 강의에서 들은 내용을 따라 적용해봤다.
saveCalculationProcess(operandFirst, operandSecond, result, operator);
저장하기 위한 몸부림을 추가하고 콘솔 기반이지만 눈으로 보기 쉽도록 System.lineSeparator()
을 추가해보았다. 이전에는 \n
을 사용했지만 이번에는 메서드를 사용해보았다.
메서드를 사용해 줄을 바꾼 이유
\n
,\r
등을 이용해 줄을 바꾸는 것은 OS마다 다르게 작동한다는 것을 알게 되어 새로운 방법을 찾아보다가System.lineSeparator()
를 알게 되었다.
System.lineSeparator()
는 어떤 OS든 해당 OS에 맞게 개행 문자를 리턴해주기 때문에 유용하게 사용할 수 있다.
❗ 필수 기능 가이드
- Lv 1에서 구현한 App 클래스의 main 메서드에 Calculator 클래스가 활용될 수 있도록 수정
- 연산 수행 역할은 Calculator 클래스가 담당
- 연산 결과는 Calculator 클래스의 연산 결과를 저장하는 필드에 저장
- 소스 코드 수정 후에도 수정 전의 기능들이 반드시 똑같이 동작해야합니다.
// 접근 제어 변수 생성
private int operandFirst = 0;
private int operandSecond = 0;
private String pastRecords = "0";
private String menu;
private boolean MenuStart;
private boolean MenupastRecords = false;
private Calculator calc = new Calculator();
private Scanner sc = new Scanner(System.in);
GuideProcessing
public
으로 했다가 클래스 모르면 못나가는 방
과 객체 활용 세션
을 듣고 오자마자 private
로 변경했다!// 연산과정 및 결과 저장 List 생성
private ArrayList<Integer> saveOperandsFirst = new ArrayList<>();
private ArrayList<Integer> saveOperandsSecond = new ArrayList<>();
private ArrayList<String> operators = new ArrayList<>();
private ArrayList<Double> results = new ArrayList<>();
Calculator
Character
가 아닌 String
을 활용했다.String
을 사용했다가 Char
를 써보라는 항목을 읽고 변경했지만 이번에는 검색을 해보다가 String을 써도 괜찮겠다는 생각에 String
으로 해보았다.❗ 필수 기능 가이드
- App 클래스의 main 메서드에서 Calculator 클래스의 연산 결과를 저장하고 있는 컬렉션 필드에 직접 접근하지 못하도록 수정 (캡슐화)
- 간접 접근을 통해 필드에 접근하여 가져올 수 있도록 구현합니다. (Getter 메서드)
- 간접 접근을 통해 필드에 접근하여 수정할 수 있도록 구현합니다. (Setter 메서드)
- 위 요구사항을 모두 구현 했다면 App 클래스의 main 메서드에서 위에서 구현한 메서드를 활용 해봅니다.
// 지정된 연산 결과 출력
public double getResult(int index) throws IndexOutOfBoundsException, EmptyListException {
// List 비어있을 경우 예외 발생
if (CheckEmptyList()) {
throw new EmptyListException("저장된 연산결과가 없습니다." + System.lineSeparator() + "==================== \uD83D\uDEA8 다시 시작 합니다! ====================");
// size 초과하는 인덱스를 입력시 예외 발생
} else if (CheckListSize(index)) {
throw new IndexOutOfBoundsException();
} else {
return results.get(index - 1);
}
}
// 저장된 모든 연산결과 출력
public void getAllResult() throws EmptyListException {
// List 비어있을 경우 예외 발생
if (CheckEmptyList()) {
throw new EmptyListException("저장된 결과값이 없습니다." + System.lineSeparator() + "==================== \uD83D\uDEA8 다시 시작 합니다! ====================");
} else {
for (int i = 0; i < results.size(); i++) {
System.out.println(i + 1 + "번째 결과값 : " + results.get(i));
}
}
}
// 지정된 연산식 출력
public String getcalCulationProcess(int index) throws IndexOutOfBoundsException, EmptyListException {
// List 비어있을 경우 예외 발생
if (CheckEmptyList()) {
throw new EmptyListException("저장된 연산식이 없습니다." + System.lineSeparator() + "==================== \uD83D\uDEA8 다시 시작 합니다! ====================");
// size 초과하는 인덱스 입력시 예외 발생
} else if (CheckListSize(index)) {
throw new IndexOutOfBoundsException();
} else {
calculationProcess = saveOperandsFirst.get(index - 1).toString() + " " + operators.get(index - 1) + " " + saveOperandsSecond.get(index - 1).toString()
+ " = " + results.get(index - 1).toString();
return calculationProcess;
}
}
// 저장된 모든 연산식 출력
public void getAllcalCulationProcess() throws EmptyListException {
// List 비어있을 경우 예외 발생
if (CheckEmptyList()) {
throw new EmptyListException("저장된 연산식이 없습니다." + System.lineSeparator() + "==================== \uD83D\uDEA8 다시 시작 합니다! ====================");
} else {
for (int i = 0; i < saveOperandsFirst.size(); i++) {
System.out.println(i + 1 + "번째 연산 과정 : " + saveOperandsFirst.get(i).toString() + " " + operators.get(i) + " " + saveOperandsSecond.get(i).toString()
+ " = " + results.get(i).toString());
}
}
}
// 가장 최근 연산 결과 반환
public double getRecentResult() throws EmptyListException {
// List 비어있을 경우 예외 발생
if (CheckEmptyList()) {
throw new EmptyListException("저장된 결과값이 없습니다." + System.lineSeparator() + "==================== \uD83D\uDEA8 다시 시작 합니다! ====================");
} else {
return results.get(results.size() - 1);
}
}
// 가장 최근 연산 과정 반환
public String getRecentCulationProcess() throws EmptyListException {
if (CheckEmptyList()) {
throw new EmptyListException("저장된 결과값이 없습니다." + System.lineSeparator() + "==================== \uD83D\uDEA8 다시 시작 합니다! ====================");
} else {
calculationProcess = saveOperandsFirst.get(saveOperandsFirst.size() - 1).toString() + " " + operators.get(operators.size() - 1) + " " + saveOperandsSecond.get(saveOperandsSecond.size() - 1).toString()
+ " = " + results.get(results.size() - 1).toString();
return calculationProcess;
}
}
Calculator
h
를 눌렀을 때에는 저장된 기록을 가져와서 출력
해야하기 때문에 getter
를 이용했다.❗ 필수 기능 가이드
- Calculator 클래스에 저장된 연산 결과들 중 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드를 구현한 후 App 클래스의 main 메서드에 삭제 메서드가 활용될 수 있도록 수정
- 키워드 :
컬렉션
- 컬렉션에서 ‘값을 넣고 제거하는 방법을 이해한다.’가 중요합니다!
// 가장 최근에 저장된 값 제거
public void removeRecentResults() throws EmptyListException {
if (CheckEmptyList()) {
throw new EmptyListException("저장된 결과값이 없습니다." + System.lineSeparator() + "==================== \uD83D\uDEA8 다시 시작 합니다! ====================");
} else {
System.out.println("가장 마지막에 연산한 " + saveOperandsFirst.get(saveOperandsFirst.size() - 1) + operators.get(operators.size() - 1)
+ saveOperandsSecond.get(saveOperandsSecond.size() - 1) + " = " + results.get(results.size() - 1) + " 가 삭제되었습니다.");
saveOperandsFirst.remove(saveOperandsFirst.size() - 1);
saveOperandsSecond.remove(saveOperandsSecond.size() - 1);
results.remove(results.size() - 1);
operators.remove(operators.size() - 1);
}
}
Calculator
.remove()
를 이용해 저장된 값 삭제작업하면서 발생한 문제와 해결방법에 대해 적어보려고 한다.
11/20(수)
까지 만들어보기엔 현재의 내가 구현할 수 있는 부분에는 한계가 있다고 느꼈다. 11/19(화)
에 오전부터 오후까지 들었던 클래스와 객체 활용 세션 덕에 변경한 몇몇 부분들이 있다.
솔직하게 말하면 알고나서 만든게 아닌, 만들고 나서 알아가며 만든 계산기라 조금 많이 부끄럽다..
그래서 과제 제출 기간이 끝나도 계산기처럼 자바 문법으로 해볼 수 있는 과제(?)들을 찾아서 해보고자 한다.
이번 과제는 구글링을 얼마나 신속하게 하는가? 를 토대로 작업한 것 같아서 내 코드라고 부르기에도 민망하고, 제출하기도 겁이 많이 났다. 과제 제출 기한이 임박해서 급하게 만든거라 개인적으로 다시 만들어보고 있어서 완성하면 추가로 올려볼 계획**이다!
수 많은 사례들 중에 내가 가장 이해하기 쉬운 것들을 찾아 따라 작성해보기도 하고 새로 작성해보기도 하면서 공부하는 방향으로 만들어진 계산기라 부끄러움이 앞서지만.. 새롭게 알게된 부분도 몇가지 있어서 나름 좋..은 시간이었다는 생각도 든다. (이런 경험도 있을 수 있지 않을까..?)
우선 index
에 대한 부분에 대해 모르는게 많은데 따라하기엔 찝찝함이랄지, 먹먹함이 있어서 스택오버플로우와 다른 글들을 살펴보며 이해해서 사용하고자 했다. 완벽하게 이해했다고 말하기엔 거짓말이고 40% 정도 이해한 상태인 것 같다.
지난 TIL에 올렸던 부분과 동일한 내용인데 평가 기준을 늦게 확인해서 커밋 컨벤션을 지킨 커밋 10회 이상 시행
을 못했다.
이 것 하나를 놓쳐서 아쉬운 것은 아니었다.
도전 기능 3레벨을 할 수 있는 레벨이 아니었고, 트러블 슈팅을 남길 정도의 적당한 구글링을 한게 아닌 구글링 의존도가 굉장히 높은 과제물이었다. 변수명이나 코드를 직관적이고 이해하기 쉽게 작성
하려고 노력했으나 갈수록 변수명이 과하게 길어졌어서 다음번에는 그러지 말자고 다짐하게되는 계기가 되었다.
Read.me는 현재 작성중에 있어서 코멘트할 부분이 없지만, 예외처리 부분도 구글링 덕에 2가지 이상 구현하게 되었다. 다시 생각해도 지금 제출한 과제물은 내 손을 제대로 탄 부분이 처음 몇 부분과 사이사이의 자잘한 부분밖에 없는 것 같다는 생각이 커서 조금 자괴감이 든다.
트러블 슈팅답지 못한 트러블 슈팅이라 많은 아쉬움이 남는다..
작업 후기
소요시간
: 3시간 (Local History 기준)
난이도
: 상
>> Java : Lv1. 클래스없는 사칙연산 계산기 GitHub Code 바로가기
>> Java : Lv2. 클래스를 적용한 사칙연산 계산기 GitHub Code 바로가기
다 만들어진 코드는 위의 깃허브에 올려놓았다.