1. gui, 후위변환,계산,우선순위, 연산자 묶음으로 클래스로 분리
- 구성




- 부족한 점
전체적인 구성을 할때부터 void로 많이 나누었기때문에 큰 문제가 안되겠다고 판단했지만, 클래스 ,enum,인터페이스와 같이 객체가 존재하고 그 마다 역할이 달라서 class로만 통일한 부분에 있어서 이게 맞는 판단인지 불분명하다.
또한 더욱 세세하게 나눌것인가와 어떻게 하면 코드를 단축시키기고 가독성을 느릴 것 인가에 있어서 부족함이 많이 느꼈으며,lv3를 진행할 땐 좀 더 가독성에 중점을 두기 위해 공부를 할 필요를 느꼈다.
2. 예외처리
- 목적 EmptyStackException과ArithmeticException 예외처리
- why? EmptyStackException은 아무것도 입력되지 않아서 발생하는 문제, ArithmeticExceptio은 무한의 값을 가지는 경우의 문제이기 때문에 exception이 발생한다. 이를 예외처리하여 사용하였을 때 문제에 대해 인지하고 해결하기 위해서 이다.
- 구성
public class Solution extends Calculator{
private String solute(String str) {
Stack<Double> stack = new Stack<>();
stack.push(0.0); // 음수 연산 방지용 (0을 미리 넣어둠)
String[] tokens = str.split(" "); // 공백을 기준으로 후위 표기법 토큰 분리
for (String token : tokens) {
if (token.matches("-?\\d+(\\.\\d+)?")) { // 공부
stack.push(Double.parseDouble(token));
}
else { // 연산자인 경우
Double firstInt;
Double secondInt;
try {
secondInt = stack.pop();
firstInt = stack.pop();
stack.push(releaseCalculation(firstInt, secondInt, Operator.fromChar(token.charAt(0)))); // 연산 결과를 스택에 다시 푸시
} catch (EmptyStackException e) {
return "입력이 필요합니다";
} catch (ArithmeticException e) {
return "0으로 나눌 수 없습니다";
}
}
}
return Double.toString(stack.pop()); // 최종 결과 반환
}
- 트러블 슈팅
배경 : 예외처리와 예외발생시 사용자에게 알리는 방식에 대한 고민
발단 : 간단하게 예외발생 시 print를 사용해서 문제를 알렸지만 이가 부자연스럽다고 생각하였습니다.
전개: 각각에 연산에서 예외 발생시 textfiled를 해당하는 문자를 return을 하는 함수를 고민하였지만 코드가 길어짐
으로 가독성이 안 좋아진다는 문제를 확인하였습니다.
절정 : solution에서 rerurn을 하면 String 자료형으로 return된다는 사실을 알게됐고, 이를 사용한다면 textfiled를 원하는 형태로 알림이 가능해진다것을확인 적용하였습니다.
결말 : 예외처리는 상위 객체에서도 가능하다는 사실과 rerurn 값이 어디로 rerurn이 되는지에 대한 확인은 코드를 불필요하게 늘릴필요가 없으므로 꾸준한 체크는 필수라는 것을 잊으면 안된다.!!!!
3. enum
- 목적 enum을 사용하여 연산자를 클래스로 분리하기
- why? enum을 사용하여 가독성을 높이며,타입 안정성을 높이기 위해서이다.
- 구성
enum Operator {
ADD('+'), SUBTRACT('-'), MULTIPLY('*'), DIVIDE('/'), MODULO('%');
private final char symbol;
Operator(char symbol) {
this.symbol = symbol;
}
public char getSymbol() {
return symbol;
}
public static Operator fromChar(char ch){
for(Operator operator: Operator.values()){
if(ch == operator.symbol){
return operator;
}
}throw new IllegalArgumentException("계산기에 존재하는 수식이 아닙니다.");
}
}
- 부족한 점
일단 enum에 대한 이해가 부족하기에 제대로된 사용이 되었다고는 어렵다. 왜냐하면 enum을 사용하여 메소드를 포함시키는 경우도 존재하며 또한 다른 케이스에선 적용이 어렵다는 점을 미뤄봤을 때 추후에 다시 볼 때 부족한 점을 찾을 수 있
을 것이라고 추측되기 때문이다.
4. 합계,평균
- 목적 결과창을 통하여 stream&lamda 사용한 합계,평균 표현
- why? stream&lamda을 통해 해당 기능에 코드를 간략하게 표현이 가능하고, stream&lamda을 사용함으로 본인의 실력을 늘릴 수 있는 기회이기 때문이다.
- 구성
package lv3;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
import static java.util.stream.Collectors.joining;
public class GuiSetting extends JFrame implements ActionListener, KeyListener {
// 입력 필드 관련 변수
String textField = ""; // 연산식을 저장하는 문자열
JTextField jTextField = new JTextField(textField, 29); // 메인 입력 필드
JTextField resultField = new JTextField(textField, 3); // 최근 계산 결과 표시 필드
JTextArea resultsField = new JTextArea(10, 32); // 모든 결과 기록을 표시하는 텍스트 영역
Postfix postfix = new Postfix();
Solution solution = new Solution();
// 창의 제목
String title = "사칙 연산 계산기";
// 계산 결과 저장 리스트
List<String> storeList = new ArrayList<>();
// 결과값을 누적할 문자열
String storeListor = "";
// 버튼 클릭 이벤트 처리
@Override
public void actionPerformed(ActionEvent e) {
String button = e.getActionCommand(); // 클릭된 버튼의 텍스트 가져오기
if (button.equals("AC")) { // 전체 지우기
textField = "";
jTextField.setText(textField);
storeListor="";
storeList=new ArrayList<>();
resultsField.setText("");
resultField.setText("");
}
else if (button.equals("=")) { // 계산 실행
String yardesult = postfix.releasePostfix(textField); // 중위 → 후위 변환
textField = solution.releaseSolution(yardesult);// 후위 연산식 계산 결과 반환
jTextField.setText(textField);
if (jTextField.getText().equals("입력이 필요합니다")||jTextField.getText().equals("0으로 나눌 수 없습니다")){
textField = "";
}
else{
storeList.add(textField); // 계산 결과 저장
resultField.setText(storeList.get(storeList.size() - 1)); // 최근 결과 표시
textField = "";
}
}
else if (button.equals("결과값")) { // 저장된 결과값 출력
storeListor=storeList.stream().collect(joining("\n"));
resultsField.setText(storeListor);
}
else if (button.equals("최근 결과 삭제")) { // 마지막 결과 삭제
if (!storeList.isEmpty()) {
storeList.remove(storeList.size() - 1);
// storeListor=storeList.stream().reduce("",(a,b)->a+"\n"+b);//스트림 람다 적용
storeListor=storeList.stream().collect(joining("\n"));
resultsField.setText(storeListor);
}
}
else if (button.equals("C")) { // 한 글자 지우기
if (!textField.isEmpty()) {
textField = textField.substring(0, textField.length() - 1);
jTextField.setText(textField);
}
} else if (button.equals("합계")) {
if (!storeList.isEmpty()){
double sumList = storeList.stream().mapToDouble(Double::parseDouble).sum();
resultsField.setText(Double.toString(sumList));
System.out.println("sumList = " + sumList);
}
} else if (button.equals("평균")) {
if (!storeList.isEmpty()) {
double avgList = storeList.stream().mapToDouble(Double::parseDouble).average().getAsDouble();
resultsField.setText(Double.toString(avgList));
System.out.println("avgList = " + avgList);
}
} else if (button.equals("최근값")) {
if(!storeList.isEmpty()){
textField=storeList.get(storeList.size()-1);
jTextField.setText(textField);
}
} else { // 숫자 및 연산자 입력
textField = textField.concat(button);
jTextField.setText(textField);
}
}
// 키 입력 이벤트 처리 (타이핑)
@Override
public void keyTyped(KeyEvent e) {
char keyChar = e.getKeyChar();
if (Character.isDigit(keyChar) || "+-*/%()".indexOf(keyChar) != -1) {
textField = textField.concat(String.valueOf(keyChar));
jTextField.setText(textField);
}
e.consume(); // 기본 입력 차단
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) { // 백스페이스 처리
if (!textField.isEmpty()) {
textField = textField.substring(0, textField.length() - 1);
jTextField.setText(textField);
}
} else if (e.getKeyCode() == KeyEvent.VK_ENTER) { // 엔터 입력 시 계산 실행
String yardesult = postfix.releasePostfix(textField); // 중위 → 후위 변환
textField = solution.releaseSolution(yardesult);// 후위 연산식 계산 결과 반환
jTextField.setText(textField);
textField = "";
}
e.consume(); // 기본 입력 차단
}
}
- 트리블 슈팅
배경 : 스트림과 람다에 적용방식
발단 : 아래에 있는 블로그를 살펴보면서 기능에 맞는 스트림과 람다를 적용
전개: 우선적으론 join을 사용하였는데 추가적인 검색으로 찾을 예외처리가 가능한 reduce를 발견하였다
절정 : !storeList.isEmpty())를 우선적으로 처리하기 때문에 storeList가 null이 가능성이 적다고 판단하였고, 통일성을 위해 join을 선택하였다. 이 선택이 맞는지는 지금도 잘 모르겠다.
결말 : 기능적으로 예외처리를 함으로써 사용자에게 표현 해줄 수 있다면 !storeList.isEmpty()을 하는 선택은 옳은 선택이 아니라고 판단이 된다. 내가 사용하였을 때 원인을 모르면 답답한 경험을 겪어봤기에 다음과 상황(예외처리를 막아버리는 상황과 예외처리를함으로 표현하는 상황)들을 고려함과 동시에 thread를 어떻게 사용하는지를 고민하게 됐다.
5. 참조
https://velog.io/@mooh2jj/Java-Enum%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0
[Java] Enum을 사용하는 이유와 활용법
Java enum은 제한된 값 목록을 갖는 타입입니다. enum은 다음과 같은 이점을 갖습니다.enum은 컴파일 타임에 타입 안정성을 보장합니다. 특정 범위의 값만 사용 가능하므로 컴파일 오류나 런타임 예
velog.io
[Java] 열거 타입(Enum) 사용법 및 예제
프로그래밍 언어에서는 열거 타입을 제공하는 경우가 많다. 요일이나 계절처럼 한정된 개수의 값들을 하나로 묶어서 사용하고 싶을 때, 열거 타입을 사용하면 편하다. 자바에서는 Enum 타입을 통
hbase.tistory.com
https://dd-developer.tistory.com/99
[JAVA] 스트림은 주의해서 사용하자
스트림 (Stream) 스트림은 다량의 데이터 처리 작업을 위해 자바 8에서 등장했다. 컬렉션 처리를 위한 새로운 API라고 할 수도 있다. 스트림 구조 스트림은 초기 데이터→ 중간 연산 → 최종 연산 의
dd-developer.tistory.com
[JAVA]람다와 스트림(Lambda & Stream)
1. 람다식(Lambda Expression) 1.1. 람다식이란? - 함수(메서드)를 간단한 식으로 표현하는 방법 - 익명 함수(이름이 없는 함수) - 함수와 메서드의 차이 - 근본적으로 동일. 함수는 일반적 용어. 메서드는
bombichun.tistory.com
6. github
'TIL' 카테고리의 다른 글
TIL 17 -코드 가타: (0) | 2025.03.06 |
---|---|
TIL 15 계산기 2차 (0) | 2025.03.04 |
TIL14 계산기-1차 (0) | 2025.02.27 |
TIL-13 자바의 프로젝트 관리(이름, 패키지,클래스) (0) | 2025.02.27 |
TIL12 - 컴퓨터의 기억방식 (1) | 2025.02.26 |