seaking110 님의 블로그
계산기 만들기 본문
요구사항 정의 및 설계
숫자를 2개 입력 문자를 1개 입력 그 후 해당 숫자와 문자들로 사칙연산을 계산한 뒤 결과값 반환하기
- App 클래스의 main 메서드에서는 입력만을 받아오는 역할
- calculator 클래스를 활용해서 연산 수행 담당
- 연산 결과를 컬렉션으로 저장 또한 Calculator 클래스가 컬렉션 타입 필드를 가지고 있어야함
고민 사항
Lv 1
- 계산기에서 + - *까지 리턴 값이 int형인데 /에선 double형으로 리턴해줘야한다.
- 일관성을 위해 + - * 모두 실수로 반환하자
Lv 2
- switch의 default에 연산기호가 아닌 숫자가 들어왔을 때 오류 처리를 해주고 continue를 해줬엇는데 클래스를 나누고 리턴 값을 줘야할 때는 어찌할지 고민이다.
- 2단계에선 예외를 신경쓰지 말라는 피드백을 받고 삭제
- Lv 3에서는 입력 메서드를 따로 만들고 안에서 무한루프를 돌고 numberFormatException을 이용해서 예외 처리
- 예외 처리에 대한 부분은 많이 진행하지 않아봤는데 DivideException 이라는 예외 클래스를 만들고 두번째 숫자가 0이면 에러가 발생하게 만들었다. 또한 연산기호가 아닌 다른 것을 입력했을 때 예외 처리를 하려고 예외 클래스를 하나더 만들었더니 Throw로 해당 메소드에 연결이 안된다.
- 방법을 찾아보니 하나의 큰 Exception 클래스를 만들고 두개의 별도의 클래스로 상속하여 연결하여 관리하기 vs 다중 예외 클래스를 하나로 통합하여 만들기
Lv 3
- 입력을 받을때 double이나 int로 받았을때 메서드의 리턴 값이 문제다 T로 리턴을 해야할 텐데 double 값을 다시 T로 변경해서 리턴하는 방법이 있나?
- 전역 변수로 타입을 지정해서 할 수 있겠지만 해당 계산기가 어떤 타입인지 알 수 있는 방법이 없을까?
- cal을 와일드 카드로 지정해주고 시작했더니 아래에서 해결이 안된다 어떻게 해결할까?
- 같은 클래스에서 필드 접근 방식 고민
- 아래 쪽 코드는 setLageResult 메서드를 만들어서 이용
- 캡슐화가 되고 재사용성이 높기때문에 아래 코드 채용
public List<Double> largeResult(double d) {
largeResult.clear();
saveResult.stream().filter(f-> f > d).forEach(f -> largeResult.add(f));
return getLargeResult();
}
vs
public List<Double> largeResult(double d) {
largeResult.clear();
saveResult.stream().filter(f-> f > d).forEach(this::setlargeResult);
return getLargeResult();
}
튜터님 피드백
- 쓸모 없는 파일 삭제 및 정리
- groovy 쪽에 쓸모 없는 파일이 존재 했다.
- 필요하지 않는 파일을 push 하는 것은 협업할 때를 위해 항상 조심하자
- 코드를 가독성 높게 쓰고 자바 문법 준수
- 제어문 사용 시 띄어쓰기를 잘 안하는 버릇이 있다. 띄어쓰기에 신경 쓰자!
- else를 바로 뒤에 배치하여 논리적인 조건의 처리를 한 흐름으로 보여주기 편하고 가독성과 else가 독립적으로 쓰이지 않도록 하여 오류를 방지해준다!
if (num2 == 0) {
System.out.println("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
} else {
double result = (double) num1 / num2;
System.out.println("결과: "+ result);
}
- 예외 클래스를 생성할 때 Exception 보단 RuntimeException을 상속!
- Exception
- checked Exception으로 반드시 처리해야하는 예외
- 코드가 복잡, 개발자가 매번 처리 코드 작성 필요
- 예외가 반드시 처리되어야 하는 상황에 적합 (파일 입출력, 네트워크 통신)
- RuntimeException
- Unchecked Exception으로 예외처리 강제 x
- throws 선언 없이 사용 가능, 코드가 간결
- 개발자가 예외를 의도적으로 처리하지 않아도 되지만 프로그램이 종료될 가능성 존재
- Exception
- 입력 받는 부분을 try catch 문을 사용하여 모두 처리
- 가독성이 너무 떨어져서 입력 받는 메소드를 따로 만들어서 3단계에서 구현
- 2단계에선 try-catch문 사용을 필수로 하지 않으니 삭제할 것
- Arraylist 말고 list로 선언
- 아래 처럼 해도 틀린 것은 아님 하지만 List는 인터페이스이고 ArrayList는 클래스.
- 보다 큰 범위로 시작해야 코드의 유연성이 높아 유지보수성이 높아짐
- 다형성을 활용하여 더 범용적으로 동작 가능
- 구현보다는 인터페이스에 의존하는 자바의 객체 지향 설계 원칙에 부합
- 코드의 추상화 수준이 높아지고 유연해짐
// o
private List<Double> saveResult = new ArrayList<>();
// x
private ArrayList<Double> saveResult = new ArrayList<>();
- 더욱 가독성 좋은 개선된 case문으로 람다식 사용
- java 14 이후부터 사용 가능
- yield 키워드를 이용해서 블록 내부에서 반환 값을 설정
return switch (op) {
case '+' -> add(num1, num2);
case '-' -> subtract(num1, num2);
case '*' -> multiply(num1, num2);
case '/'-> {
if (num2 == 0) {
throw new DivideException();
}
yield divide(num1, num2);
}
default -> throw new InvalidInputValueException();
};
enum을 처음 써본다! 쓰기전에 정리해보자
ENUM
- 열거 타입이라고도 하며 한정된 값만을 갖는 데이터 타입을 의미
- 자바에서 서로 연관 있는 상수를 관리하기 위해 사용
- 요일, 순위, 성적 등
public enum Week {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
- 열거 타입도 데이터 타입의 일종으로 변수를 선언 및 대입 가능
Week today = Week.SUNDAY;
System.out.println(today.name); // SUNDAY
- valutOf
- 열거 객체의 상수명과 동일한 문자열을 입력받아 일치하는 것을 반환
Week tommorow = Week.valueOf("MONDAY");
System.out.println(tommorow); // MONDAY
- 상수와 연관된 추가적인 데이터를 상수 자체에 포함하여 관리할 수 있다.
- 일반적인 인스턴스 필드를 사용하는 것 처럼 생성자를 정의하면 된다.
- 또한 getter setter 또한 제공하므로 이를 이용해서 계산기를 만들어보자
- enum을 이용해서 계산기를 만들자
- 기존에는 if문을 사용해서 +가 들어오면 PLUS를 붙이는 식으로 코딩했다.
- 하지만 이런 경우 유지 보수성이 심각하게 떨어지고 ENUM을 사용하는 의미가 없었다
- 그래서 상수에 연산자를 나타내는 속성을 추가했다
public enum OperatorType {
PLUS('+'),
MINUS('-'),
MULTIPLY('*'),
DIVIDE('/');
}
- 또한 메서드를 하나 만들어서 값을 비교하여 해당 ENUM 인스턴스를 반환하게 코딩했다
public char getType() {
return type;
}
// 해당 메서드가 특정 인스턴스에 의존하지 않으니 static을 추가했고
// values를 사용해서 enum 클래스를 순회하여 같은 값이 있으면 해당 데이터를 리턴
// 만약 없다면 널 값 리턴
public static OperatorType findOp(char op) {
for (OperatorType operator : values()) {
if (operator.getType() == op) {
return operator;
}
}
return null; // 유효하지 않은 연산자일 경우
}
- 제네릭을 이용하자
- 먼저 기존 계산기 클래스에 제네릭을 넣고 들어오는 모든 부분과 계산 부분의 값을 제네릭화 했다.
public class ArithmeticCalculator <T extends Number>{
public double calculate(T num1, T num2, OperatorType op) {
return switch (op) {
case PLUS -> add(num1, num2);
case MINUS -> subtract(num1, num2);
case MULTIPLY -> multiply(num1, num2);
case DIVIDE-> {
if (num2.doubleValue() == 0) {
throw new DivideException();
}
yield divide(num1, num2);
}
};
}
//사칙 연산 메서드
public double add(T num1, T num2) {
return num1.doubleValue() + num2.doubleValue();
}
public double subtract(T num1, T num2) {
return num1.doubleValue() - num2.doubleValue();
}
public double multiply(T num1, T num2) {
return num1.doubleValue() * num2.doubleValue();
}
public double divide(T num1, T num2) {
return num1.doubleValue() / num2.doubleValue();
}
}
- 결과 값을 저장하는 리스트 부분도 바꿨다가 낭패를 봤다 결과 값은 무조건 double이다!
'Today I Learned' 카테고리의 다른 글
| 키오스크 과제 1일차 (0) | 2025.01.13 |
|---|---|
| 계산기 만들기 트러블 슈팅 (0) | 2025.01.10 |
| 모던 자바 (0) | 2025.01.08 |
| Thead (1) | 2025.01.07 |
| Generic (0) | 2025.01.06 |