package calculator;
import java.util.LinkedList;
public class Calculator {// 연산 결과를 저장하고 있는 컬렉션 필드에 직접 접근하지 못하도록 수정
private LinkedList<Integer> resultList; // private 접근 제어자 사용
// 연산 결과를 저장하기 위한 컬렉션 선언 및 생성
private LinkedList<Double> circleArea; // 원의 넓이 결과를 저장하는 필드 선언
private static final double Pi = 3.14; // 원의 지름 값은 3.14로 변하지 않는 값이기 때문에
// static final을 이용하여 프로그램에서 변경되지 않는 값을 정의
// 6. 생성자를 통해 연산 결과를 저장하고 있는 컬렉션 필드가 초기화 되도록 수정
public Calculator() {
resultList = new LinkedList<>(); // 생성자에서 컬렉션 필드를 초기화, 항상 빈리스트로 시작
circleArea = new LinkedList<>(); // 원의 넓이 결과 필드 초기화
}
public LinkedList<Integer> getResultList() { //resultList getter 메서드 이름의 규칙 : get + 필드 이름
return resultList;
}
public void setResultList(LinkedList<Integer> resultList) { // setter 규칙 : set + 필드 이름
this.resultList = resultList;
}
public LinkedList<Double> getCircleArea() { // circleResult getter
return circleArea;
}
public void setCircleArea(LinkedList<Double> circleArea) { // setter
this.circleArea = circleArea;
}
public int calculate(int firstNumber, int secondNumber, char operator) throws InputErrorException {
int result = 0;
switch (operator) {
case '+' : // 사칙 연산을 수행 후 result에 저장
result = firstNumber + secondNumber;
break;
case '-' :
result = firstNumber - secondNumber;
break;
case '*' :
result = firstNumber * secondNumber;
break;
case '/' :
if (secondNumber == 0) // 나눗셈 연산에서 두번째 정수에 0이 입력되는 경우 Exception 발생
throw new InputErrorException("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
else
result = firstNumber / secondNumber;
break;
}
return result; // 사칙연산된 결과값을 리턴
}
// 4. Calculator 클래스에 저장된 연산 결과들 중
// 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드를 구현한 후 App 클래스의 main 메서드에 삭제 메서드가 활용될 수 있도록 수정
public void removeResultList() { // 공용 메서드 선언 , 반환값은 없고(void), 파라미터도 받지 않는다.
resultList.remove(0);
}
// 5.Calculator 클래스에 저장된 연산 결과들을 조회하는 기능을 가진 메서드를 구현한 후
// App 클래스의 main 메서드에 조회 메서드가 활용될 수 있도록 수정합니다.
public void inquiryResultList() { // 공용 메서드 선언
for (int results : resultList) { //리스트에 저장된 배열의 수만큼 순회하면서 반복
System.out.print(results + " "); // 결과 값을 한칸씩 띄어주기 위해서
}
System.out.println(); // 조회 값과 exit문이 겹쳐서 나오는 것을 막기 위해서
}
// 7. Calculator 클래스에 반지름을 매개변수로 전달받아 원의 넓이를 계산하여 반환해주는 메서드를 구현
public double calculateCircleArea(int radius) { // 원의 넓이를 구하는 메서드, 매게변수 정수 반지름을 받는다
return Pi * radius * radius; // 반지름* 반지름 *pi = 원의 넓이
}
// 원 넓이 조회
public void inquiryCircleArea() {
for (double results : circleArea) {
System.out.print(results + " ");
}
System.out.println();
}
}
private LinkedList<Double> circleArea;
원의 넓이 값은 실수형으로 나오므로 Double
객체를 저장하는 원의 넓이 결과를 저장하는 필드를 생성
private static final double Pi = 3.14;
파이값은 변하지 않는 값이기 때문에 final
로 지정
public LinkedList<Double> getCircleArea() { // circleResult getter
return circleArea;
}
public void setCircleArea(LinkedList<Double> circleArea) { // setter
this.circleArea = circleArea;
}
private
접근 제어자를 사용했기 때문에 외부에서 circleArea
필드에 접근하게 하기 위해서 getter와 setter 메서드를 활용
public double calculateCircleArea(int radius) { // 원의 넓이를 구하는 메서드, 매게변수 정수 반지름을 받는다
return Pi * radius * radius; // 반지름* 반지름 *pi = 원의 넓이
}
// 원 넓이 조회
public void inquiryCircleArea() {
for (double results : circleArea) {
System.out.print(results + " ");
}
System.out.println();
}
원의 넓이를 구하는 메서드, 매게변수 정수 반지름(int radius)을 받는다.
package calculator;
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Scanner;
public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Calculator calculator = new Calculator(); // Calculator 클래스의 인스턴스를 생성
while (true) {
System.out.print("첫 번째 숫자를 입력하세요: ");
//Scanner를 사용하여 양의 정수를 입력받고 변수에 저장합니다.
int firstNumber = sc.nextInt();
System.out.print("두 번째 숫자를 입력하세요: ");
int secondNumber = sc.nextInt();
sc.nextLine(); // nextInt();로 인해 남아있는 것을 없애줌
System.out.println("계산기 옵션 선택 (번호 입력) : 1. 사칙연산, 2. 원의 넓이 ");
switch (sc.nextInt()) {
case 1:
try {
System.out.print("사칙연산 기호를 입력하세요: "); // +,-,*,/
// charAt(idx) : charAt 메서드는 매개변수로 char 타입으로 변환 하고자하는 문자열의 위치를 받는다.
char operator = sc.next().charAt(0);
int result = calculator.calculate(firstNumber, secondNumber, operator);
// calculator.calculate()를 호출하여 계산을 하고 결과를 result에 저장
System.out.println("결과: " + result);
calculator.getResultList().add(result);
} catch (InputErrorException e) { // 잘못된 입력이 발생할시 이를 잡아서 에러 메세지를 출력
System.out.println(e.getMessage());
}
System.out.print("가장 먼저 저장된 연산 결과를 삭제하시겠습니까? (remove 입력 시 삭제)");
if (sc.next().equals("remove")) {
calculator.removeResultList(); //"remove" 입력시 가장 첫번째 입력값 제거
// calculator 클래스에 공용 메서드를 호출
}
System.out.print("저장된 연산결과를 조회하시겠습니까? (inquiry 입력 시 조회)");
if (sc.next().equals("inquiry")) {
calculator.inquiryResultList(); // 결과 조회
}
sc.nextLine(); //버퍼에 남아있는 개행문자 제거
System.out.print("더 계산하시겠습니까? (exit 입력 시 종료)");
String exit = sc.nextLine();
if (exit.equals("exit")) {
break;
}
// 7. 원의 넓이 구하기
case 2:
System.out.print("원의 반지름 입력 : ");
int radius = sc.nextInt(); // 원의 반지름 입력 받음
double result = calculator.calculateCircleArea(radius);
// 원의 넓이를 구하는 메서드를 호출하고 result에 저장한다
System.out.println("결과 : " + result);
calculator.getCircleArea().add(result); // 컬렉션에 결과 저장
calculator.inquiryCircleArea(); // 원의 넓이 결과 조회
sc.nextLine(); //버퍼에 남아있는 개행문자 제거
System.out.print("더 계산하시겠습니까? (exit 입력 시 종료)");
if (sc.nextLine().equals("exit")) {
break;
}
}
}
}
}
원의 넓이를 구하는 경우를 만들어 줘야 하므로 switch
문을 활용하여 case 1에는 사칙연산을 구하는 경우 case 2에는 원의 넓이를 구하는 경우를 만들어 주었다.
git hub
https://github.com/Sangmin1999/calculator/commit/913c2e09d5d3519ba1e4ed8d52252aaebf5c6d8c
public abstract class Calculator {// 연산 결과를 저장하고 있는 컬렉션 필드에 직접 접근하지 못하도록 수정
private LinkedList<Double> resultList; // private 접근 제어자 사용
// 연산 결과를 저장하기 위한 컬렉션 선언 및 생성
새롭게 만들어야 할 ArithmeticCalculator,CircleCalculator 클래스에서 부모 클래스로 상속해야 하므로 Calculator 클라스를 추상 클래스로 바꾸어 주었다.
// 추상 메서드
protected abstract double calculate(int firstNumber, int secondNumber, char operator) throws InputErrorException;
protected abstract double calculate(int radius);
자식 클래스에서 오버라이딩해서 사용하기 위한 사칙연산과 원의 넓이를 구하는 추상메서드를 각각 만들어 주었다.
package calculator;
public class ArithmeticCalculator extends Calculator { // Calculator 클래스를 상속
@Override
public double calculate(int firstNumber, int secondNumber, char operator) throws InputErrorException {
int result = 0;
switch (operator) {
case '+' : // 사칙 연산을 수행 후 result에 저장
result = firstNumber + secondNumber;
break;
case '-' :
result = firstNumber - secondNumber;
break;
case '*' :
result = firstNumber * secondNumber;
break;
case '/' :
if (secondNumber == 0) // 나눗셈 연산에서 두번째 정수에 0이 입력되는 경우 Exception 발생
throw new InputErrorException("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
else
result = firstNumber / secondNumber;
break;
}
return result;
}
// 사용하지 않는 오버라이딩 메서드를 접근제어자를 protected를 써서 접근 불가능하게 만듬
@Override
protected double calculate(int radius) {
return 0;
}
}
Calculator 부모 클래스를 상속 받는ArithmeticCalculator 자식 클래스를 생성 후 오버라이딩 메서드를 만들어 주었고 각각 사용하는 메서드는 접근 제어자를 public
, 사용하지 않는 메서드는 private
을 통해 만들어 주었다.
package calculator;
public class CircleCalculator extends Calculator{
private static final double Pi = 3.14; // 원의 지름 값은 3.14로 변하지 않는 값이기 때문에
// 사용하지 않는 오버라이딩 메서드를 접근제어자를 protected를 써서 접근 불가능하게 만듬
@Override
protected double calculate(int firstNumber, int secondNumber, char operator) throws InputErrorException {
return 0;
}
@Override
public double calculate(int radius) {
return Pi* radius *radius;
}
}
이렇게 원의 넓이를 구하는 Calculator를 상속받는 CircleCalculator 클래스를 만들어 주었다.
// 사칙연산, 원 넓이를 계산하는 두개의 객체를 각각 생성
ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculator();
CircleCalculator circleCalculator = new CircleCalculator();
사칙연산, 원 넓이를 계산하는 두개의 객체를 각각 생성해준다.
git hub
https://github.com/Sangmin1999/calculator/commit/65abaed6956928d318465656500d0c11bfdf1444
사칙연산을 하는 클래스를 각각 4개를 만들고 사칙연산 클래스 필드를 선언하고 생성해주려고 하였다.
package calculator;
public class AddOperator {
public int operatre(int firstNumber, int secondNumber) { // 메서드 구현
return firstNumber + secondNumber;
}
}
package calculator;
public class SubstractOperator {
public int operatre(int firstNumber, int secondNumber) { // 메서드 구현
return firstNumber - secondNumber;
}
}
package calculator;
public class MultiplyOperator {
public int operatre(int firstNumber, int secondNumber) { // 메서드 구현
return firstNumber * secondNumber;
}
}
package calculator;
public class DivideOperator {
public int operatre(int firstNumber, int secondNumber) { // 메서드 구현
return firstNumber / secondNumber;
}
}
public class ArithmeticCalculator extends Calculator { // Calculator 클래스를 상속
private AddOperator addOperator; // 사칙연산 클래스 필드 선언
private SubstractOperator substractOperator;
private MultiplyOperator multiplyOperator;
private DivideOperator divideOperator;
public ArithmeticCalculator() { // 부모 생성자를 자동 호출
addOperator = new AddOperator(); // 사칙연산 클래스 필드 생성
substractOperator = new SubstractOperator();
multiplyOperator = new MultiplyOperator();
divideOperator = new DivideOperator();
}
각각 만들어준 4개의 클래스를 ArithmeticCalculator 클래스에 필드를 선언해주고 생성해준다.(사칙연산 클래스들을 초기화 해주기 위해서)
git hub
https://github.com/Sangmin1999/calculator/commit/60ba4f4368269063fa2cd9f1aaab7537a431c1ec
나머지 연산 기능을 추가하기 위한 ModOperator 클래스를 만들어 주고, 사칙 연산 메서드의 형태가 4클래스 모두 같기 때문에 Operator 추상 클래스를 만들어서 사칙연산 클래스가 오버로딩 할 수 있도록 해주어야 겠다고 생각했다.
package calculator;
public class ModOperator {
public int operatre(int firstNumber, int secondNumber) { // 나머지 기능 구현
return firstNumber % secondNumber;
}
}
package calculator;
public abstract class Operator { // 오버로딩
// 사칙 연산 메서드의 형태(이름, 매게변수)가 모두 같기 때문에 추상메서드로 만들었다.
public abstract int operatre(int firstNumber, int secondNumber);
}
기존의 사칙연산 클래스들을 위의 클래스를 상속 받도록 만들어 주었다
package calculator;
public class AddOperator extends Operator { // Operator 클래스를 상속받아서 오버라이딩(재정의)을 한다
public int operatre(int firstNumber, int secondNumber) { // 메서드 구현
return firstNumber + secondNumber;
}
}
package calculator;
public class SubstractOperator extends Operator {
public int operatre(int firstNumber, int secondNumber) { // 메서드 구현
return firstNumber - secondNumber;
}
}
package calculator;
public class MultiplyOperator extends Operator {
public int operatre(int firstNumber, int secondNumber) { // 메서드 구현
return firstNumber * secondNumber;
}
}
package calculator;
public class DivideOperator extends Operator {
public int operatre(int firstNumber, int secondNumber) { // 메서드 구현
return firstNumber / secondNumber;
}
}
package calculator;
public class ModOperator extends Operator {
public int operatre(int firstNumber, int secondNumber) { // 나머지 기능 구현
return firstNumber % secondNumber;
}
}
그리고 사칙연산 계산을 구현하는 ArithmeticCalculator 클래스에서
@Override
public int calculate(int firstNumber, int secondNumber, char operator) throws InputErrorException {
int result = 0;
switch (operator) {
case '+' : // 사칙 연산을 수행 후 result에 저장
result = addOperator.operatre(firstNumber , secondNumber);
break;
case '-' :
result = substractOperator.operatre(firstNumber, secondNumber);
break;
case '*' :
result = multiplyOperator.operatre(firstNumber, secondNumber);
break;
case '/' :
if (secondNumber == 0) // 나눗셈 연산에서 두번째 정수에 0이 입력되는 경우 Exception 발생
throw new InputErrorException("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
else
result = divideOperator.operatre(firstNumber, secondNumber);
break;
case '%' :
result = modOperator.operatre(firstNumber, secondNumber);
break;
}
return result;
}
operate 메서드를 가져와서 사용했다.
이렇게 코드를 변경 후 실행은 잘 되었지만 실행과정에서 case 1 (사칙연산) 부분이 실행된 후 다시 어떤 연산을 할건지 물어보지 않고 바로 원의 넓이를 구하는 부분이 실행되는 문제가 발생하였다. 이를 해결해주기 위해서 기존의 break;
를 쓰는 곳에 대신 return
을 활용해 주었다.
while (true) {
System.out.println("계산기 옵션 선택 (번호 입력) : 1. 사칙연산, 2. 원의 넓이 ");
switch (sc.nextInt()) {
case 1:
System.out.print("첫 번째 숫자를 입력하세요: ");
//Scanner를 사용하여 양의 정수를 입력받고 변수에 저장합니다.
int firstNumber = sc.nextInt();
System.out.print("두 번째 숫자를 입력하세요: ");
int secondNumber = sc.nextInt();
System.out.print("사칙연산 기호를 입력하세요: "); // +,-,*,/
// charAt(idx) : charAt 메서드는 매개변수로 char 타입으로 변환 하고자하는 문자열의 위치를 받는다.
char operator = sc.next().charAt(0);
try {
double result = arithmeticCalculator.calculate(firstNumber, secondNumber, operator);
// calculator.calculate()를 호출하여 계산을 하고 결과를 result에 저장
System.out.println("결과: " + result);
arithmeticCalculator.getResultList().add(result);
} catch (InputErrorException e) { // 잘못된 입력이 발생할시 이를 잡아서 에러 메세지를 출력
System.out.println(e.getMessage());
}
System.out.print("가장 먼저 저장된 연산 결과를 삭제하시겠습니까? (remove 입력 시 삭제)");
if (sc.next().equals("remove")) {
arithmeticCalculator.removeResultList(); //"remove" 입력시 가장 첫번째 입력값 제거
// calculator 클래스에 공용 메서드를 호출
}
System.out.print("저장된 연산결과를 조회하시겠습니까? (inquiry 입력 시 조회)");
if (sc.next().equals("inquiry")) {
arithmeticCalculator.inquiryResultList(); // 결과 조회
}
sc.nextLine(); //버퍼에 남아있는 개행문자 제거
System.out.print("더 계산하시겠습니까? (exit 입력 시 종료)");
String exit = sc.nextLine();
if (exit.equals("exit")) {
return; // case1이 끝나면 case2로 넘어가는 문제를 해결하기 위해서 break -> return으로 교체
}
break;
// 7. 원의 넓이 구하기
case 2:
System.out.print("원의 반지름 입력 : ");
int radius = sc.nextInt(); // 원의 반지름 입력 받음
double result = circleCalculator.calculate(radius);
// 원의 넓이를 구하는 메서드를 호출하고 result에 저장한다
System.out.println("결과 : " + result);
circleCalculator.getResultList().add(result); // 컬렉션에 결과 저장
circleCalculator.inquiryResultList(); // 원의 넓이 결과 조회
sc.nextLine(); //버퍼에 남아있는 개행문자 제거
System.out.print("더 계산하시겠습니까? (exit 입력 시 종료)");
if (sc.nextLine().equals("exit")) {
return;// case1이 끝나면 case2로 넘어가는 문제를 해결하기 위해서 break -> return으로 교체
}
break;
}
git hub
https://github.com/Sangmin1999/calculator/commit/381b34addff0893516d17a4d9f103a417fe55ed5
코드를 좀 더 활용해 보고 싶어서 추상메서드를 인터페이스 명으로 지정하고 오버라이딩 후 구현해 보는 과정을 진행해 보았다.
package calculator;
public interface Operator { // 오버로딩
// 사칙 연산 메서드의 형태(이름, 매게변수)가 모두 같기 때문에 추상메서드로 만들었다.
int operatre(int firstNumber, int secondNumber);
}
interface 로 Operator를 인터페이스명으로 지정해 주고
package calculator;
public class AddOperator implements Operator { // Operator 클래스를 상속받아서 오버라이딩(재정의)을 한다
public int operatre(int firstNumber, int secondNumber) { // 메서드 구현
return firstNumber + secondNumber;
}
}
기존의 사칙연산 클래스들을 implements로 상속받도록 하였다.
위험이 떠서 이유를 찾아보았더니 final를 활용하여 필드가 초기화된 이후에 변경되지 않는 것을 명시적으로 표현해주면서 코드의 명확성과 안전성을 높여주었다.
public class ArithmeticCalculator extends Calculator { // Calculator 클래스를 상속
// 초기화된 이후에 변경되지 않으므로 (final)
private final AddOperator addOperator; // 사칙연산 클래스 필드 선언
private final SubstractOperator substractOperator;
private final MultiplyOperator multiplyOperator;
private final DivideOperator divideOperator;
private final ModOperator modOperator;
최종 코드
git hub : https://github.com/Sangmin1999/calculator/commit/6f6141e7419af0cbebe65513f33238fdf7a365dd