[문제]
package com.project.personal.calculator2;
import java.util.*;
// [참고] 패키지 전체를 불러오고 싶을 때 사용
public class Calculator {
ArrayList<Integer> results = new ArrayList<Integer>();
public Calculator() {
}
public int calculate(int numOne, String operator, int numTwo) {
if (operator.equals("+")) {
return numOne + numTwo;
} else if (operator.equals("-")) {
return numOne - numTwo;
} else if (operator.equals("*")) {
return numOne * numTwo;
} else {
if (numTwo == 0) {
return Integer.MIN_VALUE;
} else {
return numOne / numTwo;
}
}
}
// [문제] this.results.add();는 적었는데, 그 다음이 막막하다.
2단계 계산기를 만들려면 '연산 결과를 저장하는 컬렉션(Collection) 타입 필드를 가진 Calculator 클래스'를 반드시 생성해야 했다. 2주 차 강의 속 숙제를 할 때도 헷갈린 부분이었는데 역시나. 소괄호 안에 대체 무엇을 쓰고, this.results.add(); 전체를 어디에 써야 하는지도 헷갈렸다. 다행이라면 '그냥 모르겠다'가 아니라 'add() 메서드를 어디서 어떻게 써야 하는지 모르겠다'처럼 질문이 구체화되었다. 이쯤 되니 튜터님을 찾아가는 데 망설임이 사라졌다.
[원인]
원인은 총 두 가지로 정리할 수 있었다.
첫째, 소괄호 안에 들어가는 매개변수, 일명 파라미터(Parameter) 개념이 100% 잡히지 않았다. 이는 클래스와 생성자, 객체를 여러 번 생성하면 해결될 듯했다. 오늘 방 탈출 세션에 참여했을 때 이미 겪어봤으니까.
둘째, 메서드가 어디서 종료되는지 인지하지 못했다. 조건식이 참이 되어 반복문이 실행되었을 때 어디서 끝나는지 몰라서 add() 위치를 정하지 못했다.
[해결]
이번에 발생한 문제는 절차 지향과 객체 지향으로 해결할 수 있었다.
1) 절차 지향 프로그래밍
package com.project.personal.calculator2;
import java.util.*;
public class Calculator {
// [1] ArrayList 선언 및 생성
ArrayList<Integer> results = new ArrayList<Integer>();
// [참고] ArrayList는 사이즈를 지정하는 것이 없어서 초기화할 필요가 없다.
public Calculator() {
}
public int calculate(int numOne, String operator, int numTwo) {
// [2] 로컬 변수 선언 및 초기화
int storage = 0;
if (operator.equals("+")) {
// [3] storage에 연산 결과 저장
storage = numOne + numTwo;
// [수정 전] return numOne + numTwo;
// [수정 후] storage = numOne + numTwo;
} else if (operator.equals("-")) {
storage = numOne - numTwo;
// [수정 전] return numOne - numTwo;
// [수정 후] storage = numOne - numTwo;
} else if (operator.equals("*")) {
storage = numOne * numTwo;
// [수정 전] return numOne * numTwo;
// [수정 후] storage = numOne * numTwo;
} else {
if (numTwo == 0) {
return -127;
} else {
storage = numOne / numTwo;
// [수정 전] return numOne / numTwo;
// [수정 후] storage = numOne / numTwo;
}
}
// [4] add() 메서드 사용하기
this.results.add(storage);
// [설명] storage에 연산 결과값이 저장되었다.
// 따라서 storage를 results에 저장해야 한다.
// [복습] 메서드는 실행되면 끝이다.
// 즉, if ~ else가 전부 실행되는 게 아니다.
// [5] storage 반환
return storage;
}
}
클래스에서 기껏 저장을 해놓고는 Main에 또 저장 관련 코드를 적으려고 했다. 분명 절차 지향 해결법과 객체 지향 해결법을 한꺼번에 들은 까닭이리라. 처음엔 'this.result.add(storage); 코드와 return storage; 코드 딱 두 줄만 맨 밑에 적으면 되잖아?' 속으로 감탄하며 좋은 줄 알았는데, 착각이었다. if문이 조금만 길어지면 복사한 코드를 잘못 붙여넣을 확률이 바로 수직 상승했다! 튜터님의 설명을 듣고 나서 왜 이 해결법이 절차 지향인지 확 와닿았다.
2) 객체 지향 프로그래밍
// [A] 클래스 Calculator에서 수정하기
package com.project.personal.calculator2;
import java.util.*;
public class Calculator {
ArrayList<Integer> results = new ArrayList<Integer>();
public Calculator() {
}
public int calculate(int numOne, String operator, int numTwo) {
if (operator.equals("+")) {
return numOne + numTwo;
} else if (operator.equals("-")) {
return numOne - numTwo;
} else if (operator.equals("*")) {
return numOne * numTwo;
} else {
if (numTwo == 0) {
return Integer.MIN_VALUE;
} else {
return numOne / numTwo;
}
}
}
// [해결 1/2] 메서드 선언하기
public void saveResults (int eachResult) {
this.results.add(eachResult);
}
}
// [B] Main에서 수정하기
package com.project.personal.calculator2;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) {
System.out.print("0 이상인 첫 번째 정수를 입력해 주세요: ");
int numFirst = sc.nextInt();
System.out.print("사칙연산 기호(+, -, *, /) 중 하나를 입력해 주세요: ");
String operator = sc.next();
System.out.print("0 이상인 두 번째 정수를 입력해 주세요: ");
int numSecond = sc.nextInt();
Calculator test = new Calculator();
int result = test.calculate(numFirst, operator, numSecond);
if (result == Integer.MIN_VALUE) {
System.out.println("나눗셈 연산에서 분모(두 번째 정수)에 0은 올 수 없습니다.");
} else {
// [해결 2/2] 클래스에서 선언한 메서드 호출하기
test.saveResults(result);
// [설명] 분모가 0일 때 결괏값은 저장할 필요가 없으므로 여기서 호출했다.
String outcome = Integer.toString(result);
System.out.println("연산한 결과는 " + outcome + "입니다.");
}
System.out.print("프로그램을 종료하시겠습니까? 종료를 원하시면 exit을 입력해 주세요: ");
String exit = sc.next();
if (exit.equals("exit")) {
System.out.println("예, 알겠습니다. 프로그램을 종료합니다.");
break;
}
}
}
}
직접 메서드를 선언하고 호출하니 왜 이 방식이 객체 지향인지 알 수 있었다. 아무리 코드가 길어져도 알맞은 위치에서 메서드를 호출하면 되니까, 확장성과 유지보수 모두 고려한 방식이라고 할 수 있었다. 객체 지향적인 프로그래밍이 무엇인지 예전보다 확실히 감이 왔다.
'Troubleshooting: 무엇이 문제였는가? > 본캠프 2주 차: 계산기 만들기' 카테고리의 다른 글
2단계: "반복문 안에서 인스턴스화를 했다고요? 갑부시군요!" (0) | 2024.11.24 |
---|---|
2단계: 반환타입이 int라서 return "문자열"이 불가능할 때 (0) | 2024.11.19 |
2단계: sc.nextLine(); vs sc.next(); (0) | 2024.11.19 |
2단계: 비교 연산자(==) vs equals() (0) | 2024.11.19 |