일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 백준 1992
- 창의충전소
- springboot
- bfs dfs
- 폴더구조
- 이영직
- 자료구조
- BFS
- React Natvive
- web view
- Navigation
- FlatList
- 휴대폰 기기
- 완전탐색
- 상속 관계 매핑
- Project Bee
- ReactNative
- multipart upload
- React Native
- 티스토리챌린지
- 버튼 활성화
- 해외 대외활동
- react
- 원복
- 노마드코더
- 경우의 수
- 오블완
- 비트마스킹
- 구현
- service 테스트
- Today
- Total
유미의 기록들
[개인 과제 -Java] 계산기 프로그램 (Level 2) 본문
📌 요구사항
1. Calculator클래스 생성
- 양의 정수 2개와 연산 기호를 매개변수로 받아 사칙연산 기능을 수행한 후 결과 값을 반환하는 메서드
- 연산 결과를 저장하는 컬렉션 타입 필드
- 나눗셈에서 분모에 0이 들어오거나 연산자 기호가 잘못 들어온 경우 예외처리
2. main메서드에서 Calculator 클래스의 컬렉션 필드에 직접 접근하지 못하도록 수정 (캡슐화)
- Getter메서드, Setter메서드 구현
3. 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드 구현 -removeResults()
4. 저장된 연산 결과들을 조회하는 기능을 가진 메서드 구현 - inquiryResults()
5. Calculator 인스턴스 생성할 때 생성자를 통해 컬렉션 필드가 초기화
6. Calculator 클래스에 반지름을 매개변수로 전달받아 원의 넓이 계산하여 반환하는 메서드 구현
- 원의 넓이 결과를 저장하는 컬렉션 타입의 필드 선언 및 생성
7. 사칙연산을 수행하는 ArithmeticCalculator클래스와 원과 관련된 연산을 수행하는 CircleCalculator클래스 2개 구현
- Calculator 클래스를 상속 받는 ArithmeticCalculator, CircleCalculator 클래스를 구현
8. 사칙연산 각각을 담당하는 AddOperator, SubtractOperator, MultiplyOperator. DivideOperator 클래스 구현 (SRP)
9. ModOperator 클래스를 추가하였을 때 소스코드의 변경은 최소화하면서 기능을 쉽게 추가할 수 있는 방법 (OCP)
⚠️ 문제 해결 과정
예외처리
num1을 0으로 나눈 경우와 연산자가 +,-,*,% 외의 다른 것이 입력되었을 때 어떻게 예외를 처리할 지
오류(error) : 시스템 레벨, 환경적인 요인으로 회복이 불가능 한 경우
예외(exception) : 미리 예상할 수 있고 대응할 수 있는 회복이 가능한 경우
Java에서 예외가 발생하면 예외클래스로부터 객체를 생성하여 해당 인스턴스를 통해 예외처리를 한다
모든 예외의 상위클래스는 Exception클래스이다.
1) 0으로 나눈 경우
java.lang.ArithmeticException : 산술 연산과정에서 오류가 있을 때 발생하는 예외, 정수는 0으로 나눌 수 없기 때문에 예외 발생
2) 잘못된 연산자를 입력했을 경우
java.lang.IllegalArgumentException : 잘못된 인수 값이 메소드에 전달 될 때 발생
try{
Calculator calculator=new Calculator();
result=calculator.calculate(num1,num2,operator);
System.out.println("결과: "+result);
}catch (ArithmeticException e){
System.out.println("0으로 나눌 수 없습니다");
continue;
}catch(IllegalStateException e){
System.out.println("연산자 형식이 맞지 않습니다");
continue;
}
Exception 처리를 하지 않았을 때는 프로그램이 강제 종료가 되었는데 catch문에 Exception을 처리하고 난 후 강제 종료되지 않고, 계속 입력을 받을 수 있었다
GIt 에러
IntelliJ에서 git add . 명령어를 입력했을 때 다음과 같은 오류가 발생했다
찾아보니깐 ./gradlew을 실행할 수 있는 권한이 없어서 발생한 것이다
./gradlew clean build -x test
위 명령어 입력 후 chmod 명령어로 gradlew 파일에 실행 권한을 부여했다
chmod +x ./gradlew
해결완료!
깃허브 커밋 메시지 한글 깨짐 현상
깃허브에 push했을 때 커밋 한글 메시지가 깨짐 현상이 나타났다
git config --global -l
위 명령어를 입력하여 현재 세팅을 확인해보았는데, i18n.commitedcoding과 i18n.logoutputencoding의 설정 자체가 안되어 있었다
git config --global i18n.commitencoding "UTF-8"
git config --global i18n.logoutputencoding "UTF-8"
해결완료!
업캐스팅이 왜 안되지
자식클래스인 CircleCalculator와 ArithmeticCalculator의 객체가 부모클래스인 Calculator 타입으로 캐스팅하려고 선언하였는데 다음과 같은 오류가 발생했다
//Calculator 인스턴스 생성
Calculator circleCalculator=new CircleCalculator();
Calculator arithmeticCalculator=new ArithmeticCalculator();
...
result=arithmeticCalculator.calculate(num1,num2,operator); //에러 발생
public abstract class Calculator {
protected ArrayList<Integer> list=new ArrayList<>();
protected ArrayList<Double> circleAreaList=new ArrayList<>();
//생성자 구현
public Calculator(){
list.clear(); // 필드 초기화
circleAreaList.clear();
}
}
그 이유는 업캐스팅한 레퍼런스 circleCalculator는 슈퍼클래스인 Calculator의 멤버만 접근 가능한데 calculate() 메소드는 ArithmeticCalculator클래스에 존재하기 때문이다
해결 방법으로 2가지가 있다
1) Calculator클래스에서 calculate()메소드를 선언해서 Override로 ArithmeticCalculator에서 구현
abstract class Calculator {
abstract public double calculate();
}
public class ArithmeticCalculator extends Calculator{
...
@Override
public double calculate(){
switch (operator){
case '+' :
AddOperator addOperator=new AddOperator();
return addOperator.operate(num1,num2);
...
}
}
}
2) 다운캐스팅을 통해 접근
result=((ArithmeticCalculator)arithmeticCalculator).calculate(num1,num2,operator); //다운캐스팅
📝 궁금한 점
업캐스팅, 다운캐스팅은 왜하는 걸까
⇒ 공통적으로 할 수 있는 부분을 만들어 간단하게 다루기 위해
다운캐스팅
업캐스팅한 객체를 다시 자식클래스 타입으로 객체로 되돌리는데 목적 (복구)
메서드를 한번만 실행할 것이라 따로 변수에 저장해둘 필요성이 없다면, 다운캐스팅을 한줄로 표현 가능
- 업캐스팅 된 객체에서 만일 자식 클래스에만 있는 메서드를 실행해야 하는 상황이 온다면, 다운 캐스팅을 통해 자식 클래스 타입으로 회귀 시킨 뒤 메서드를 실행하면 된다
- 업캐스팅한 객체를 되돌리는데 적용된다
추상클래스 vs 인터페이스
인터페이스
부모 자식 관계인 상속에 얽매이지 않고, 공통기능이 필요할 때마다 추상 메서드를 정의 해놓고 구현 → 추상클래스보다 자유롭게 붙였다 땟다 사용 가능
- 클래스와 별도로 구현 객체가 같은 동작을 한다는 것을 보장하기 위해 사용하는 것에 초점
- 인터페이스에 정의된 메서드를 각 클래스의 목적에 맞게 기능을 구현하는 느낌
- 서로 관련성이 없는 클래스들을 묶어주고 싶을 때
- 다중 상속을 통한 추상화 설계를 해야 할 때
추상클래스
- 클래스 간의 연관 관계를 구축하는 것에 초점
- 자신의 기능들을 하위 클래스로 확장시키는 느낌
- 상속 받을 클래스들이 공통으로 가지는 메소드와 필드가 많아 중복 멤버 통합을 할때
- public 이외의 접근자 선언이 필요할 때
package calculator;
import java.util.ArrayList;
import java.util.Scanner;
public class App{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
double result=0;
//연산 결과 저장 배열 생성
int n=10;
int index=0;
double results[]=new double[n];
ArithmeticCalculator arithmeticCalculator=new ArithmeticCalculator();
CircleCalculator circleCalculator=new CircleCalculator();
while(true){
System.out.print("사칙연산:1, 원의 넓이:2 >> ");
int option=sc.nextInt();
//사칙연산을 구하는 경우
if(option==1){
//1. 숫자입력
System.out.print("첫 번째 숫자를 입력하세요: ");
int num1=sc.nextInt();
System.out.print("두 번째 숫자를 입력하세요: ");
int num2=sc.nextInt();
//2. 사칙연산 기호 입력
System.out.print("사칙연산 기호를 입력하세요: ");
char operator=sc.next().charAt(0);
//3. 연산 후 결과값 출력 -> Calculator클래스의 calculate메소드 사용
try{
arithmeticCalculator.setInput(num1,num2,operator);
result=arithmeticCalculator.calculate();
System.out.println("결과: "+result);
}catch (ArithmeticException e){
System.out.println("0으로 나눌 수 없습니다");
continue;
}catch(IllegalStateException e){
System.out.println("연산자 형식이 맞지 않습니다");
continue;
}
//6. 10개 초과하는 경우, 가장 먼저 저장된 결과 삭제, 새로운 연산 결과 저장
if(index>n-1){
for(int i=1;i<index;i++) {
results[i -1] = results[i];
}
results[n-1]=result;
}
//5.연산 결과 10개 저장할 수 있는 배열 생성 및 결과 저장
else{
results[index]=result;
index++;
}
//7. 연산 결과 고정되지 않고 무한히 저장 -> Calculator클래스의 Setter 메서드 사용
arithmeticCalculator.setList(result);
//remove입력 받으면 가장 먼저 저장된 결과 삭제 -> Calculator클래스의 removeResult()사용
System.out.println("가장 먼저 저장된 연산 결과를 삭제하시겠습니까? (remove 입력 시 삭제)");
String remove=sc.next();
if(remove.equals("remove")){
arithmeticCalculator.removeResult();
}
//8. 저장된 연산 결과 전부 출력 -> Calculator클래스의 inquiryResult()사용
System.out.println("저장된 연산결과를 조회하시겠습니까? (inquiry 입력 시 조회)");
String inquiry=sc.next();
if(inquiry.equals("inquiry")){
// 배열 출력
System.out.print("배열 : ");
for(double num:results){
System.out.print(num+" ");
}
System.out.println();
//ArrayList 출력
System.out.print("ArrayList: "+arithmeticCalculator.getList());
System.out.println();
}
//원의 넓이를 구하는 경우
}else if(option==2){
//반지름 입력
System.out.print("반지름을 입력하세요: ");
int radius=sc.nextInt();
circleCalculator.setInput(radius);
double circleArea=circleCalculator.calculate();
//원의 넓이 저장
circleCalculator.setList(circleArea);
System.out.println("원의 넓이: "+circleCalculator.getList());
}
//4. 반복문 사용 (exit 입력 시 종료)
System.out.println("더 계산하시겠습니까? (exit 입력 시 종료)");
String exit=sc.next();
if(exit.equals("exit")) break;
}
}
}
package calculator;
import java.util.ArrayList;
//연산 수행 클래스
abstract class Calculator{
abstract public double calculate();
abstract public void setList(double result);
abstract public ArrayList<Double> getList();
}
package calculator;
import java.util.ArrayList;
public class ArithmeticCalculator extends Calculator{
private int num1,num2;
private char operator;
private ArrayList<Double> list=new ArrayList<>();
public void setInput(int num1,int num2,char operator){
this.num1=num1;
this.num2=num2;
this.operator=operator;
}
//리스트 조회
@Override
public ArrayList<Double> getList(){
return list;
}
@Override
public void setList(double result){
list.add(result);
}
//리스트 삭제
public void removeResult(){
list.remove(0);
}
//사칙 연산
@Override
public double calculate(){ //연산 메서드의 책임 분리
/* 연산 결과를 저장하는 컬렉션 타입 필드 선언 및 생성 */
switch (operator){
case '+' :
AddOperator addOperator=new AddOperator();
return addOperator.operate(num1,num2);
case '-':
SubtractOperator subtractOperator=new SubtractOperator();
return subtractOperator.operate(num1,num2);
case '*':
MultiplyOperator multiplyOperator=new MultiplyOperator();
return multiplyOperator.operate(num1,num2);
case '/':
//num2이 0일때 예외처리
if(num2==0){
throw new ArithmeticException();
}
DivideOperator divideOperator=new DivideOperator();
return divideOperator.operate(num1,num2);
case '%':
ModOperator modOperator=new ModOperator();
return modOperator.operate(num1,num2);
default:
throw new IllegalStateException();
}
}
}
package calculator;
import java.util.ArrayList;
public class CircleCalculator extends Calculator{
private int radius;
private ArrayList<Double> list=new ArrayList<>();
public void setInput(int radius){
this.radius=radius;
}
@Override
public double calculate(){
final double PI=3.14; // final, 정적변수 더이상 수정이 불가능하기 때문에 사용
return PI*radius*radius;
}
public void setList(double result) {
list.add(result);
}
public ArrayList<Double> getList(){
return list;
}
}
package calculator;
public class AddOperator {
public int operate(int num1,int num2){
return num1+num2;
}
}
'대외활동 기록 > 내일배움캠프' 카테고리의 다른 글
[개인 과제 -Spring숙련] JPA를 활용한 일정관리 앱 서버 설계 (Level 1) (0) | 2024.08.22 |
---|---|
[개인 과제 - Spring입문] 일정 관리 앱 서버 (0) | 2024.08.16 |
[팀 과제 - Java] 캠프 관리 프로그램 (0) | 2024.08.05 |
[개인 과제 - Java] 계산기 프로그램 (Level 3) (0) | 2024.08.02 |
[개인 과제 -Java] 계산기 프로그램 (Level 1) (0) | 2024.07.31 |