Skip to content

Conversation

@yuminhwan
Copy link

📌 과제 설명

  • 객체지향적인 코드로 계산기 구현하기
    • 정규식을 사용하여 입력값 예외 처리를 하였습니다.
    • 우선순위의 경우 후위표기식으로 바꾼 후 연산하도록 하였습니다.
      • 괄호는 고려하지 않았습니다.
        • 피드백을 받으면서 추가하도록 하겠습니다.
    • 연산 결과는 최대 소수 2자리까지 나오도록 하였습니다.
    • 잘못된 값을 입력할 경우 다시 입력받을 수 있도록 하였습니다.

👩‍💻 요구 사항과 구현 내용

Console

  • 입출력을 담당합니다.

Calculator

  • 전체적인 흐름을 제어합니다.
  • 만약 입력값이 잘못되었다면 다시 입력할 수 있습니다.
  • 1번 요청이 들어온 경우 저장되어 있는 연산결과를 출력합니다.
    • 만약 없을 경우 결과가 없다는 메시지를 출력합니다.
  • 2번 요청이 들어온 경우 계산식을 입력받습니다.
    • 받은 값을 PostfixExpression에 주입하고 계산을 요청합니다.
    • 반환받은 결과를 소수점 2자리까지만 절삭한 뒤 repository에 저장하고 출력합니다.
  • 3번 요청이 들어온 경우 프로그램을 종료합니다.

MemoryResultRepository

  • 연산 결과를 List형으로 저장합니다.
  • 연산 결과를 반환해야 할 때 저장된 값이 없다면 Optinal.empty를 반환합니다.

PostifixExpression

  • 문자열로 된 식을 주입받습니다.
    • 만약 잘못된 식이라면 예외를 던집니다.
    • 알맞은 식이라면 공백으로 기준으로 나눈 뒤 후위표기식으로 바꿔 List형으로 저장합니다.
  • 저장된 식을 통해 계산합니다.

OperatorType

  • 사칙연산에 대한 로직을 담당합니다.

✅ PR 포인트 & 궁금한 점

1. TDD 관련

  • 작은 단위 부터 TDD를 통해 구현하다보니 리팩토링을 통해 해당 기능을 다른 클래스에게 부여하거나 클래스를 분리해야할 때가 있습니다.
    • 이런 경우 그저 작성한 테스트를 해당 클래스의 테스트 코드에 옮겼는 데 맞는 방식인가요?
  • 또한, 작은 단위의 기능부터 테스트코드를 짜다보니 후에 가서는 해당 기능이 public이 아닌 private로 바꿔야 할 경우 작성한 테스트 코드를 삭제해도 되나요?
    • 저는 private 메서드라도 pulbic 메서드에서 테스트가 된다고 생각해서 삭제했는 데 멘토님의 생각이 궁금합니다.
  • 마지막으로 TDD 방식을 하다보니 객체지향적으로 설계하는 과정이 힘든 것 같습니다. ( 인터페이스를 설계하고 다형성을 생각하는? )
    • 이 경우 조금의 설계는 하고 TDD방식으로 구현하는 것이 좋을까요?

2. 클래스 분리를 명확하게 하지 못한 것 같습니다.

  • OperatorType에서 연산자를 입력받아 해당하는 타입을 반환하도록 하였습니다.

    • 계산하는 메서드같은 경우 OperatorType안에서 연산자를 입력받아 타입을 찾고 계산하는 방식이 맞는 건지 아니면 외부에서 OperatorType을 연산자를 통해 반환받고 OperatorType을 통해 계산을 하는 방식이 맞는 건지 궁금합니다.
    // OperatorType 안에서 찾고 계산 실시 
    public static double calculate(String operator, double firstNumber, double secondNumber) {
        return from(operator).expression.apply(firstNumber, secondNumber);
    }
    
    // 외부에서 OperatorType을 찾아 해당 Type으로 계산 실시
    public static double calculate (double firstNumber, double secondNumber) {
        return expression.apply(firstNumber, secondNumber);
    }
  • 처음 구현할 때 입력받은 식을 후위표기식으로 바꿔주는 기능을 Paser라는 클래스를 통해 해주었습니다.

    • 하지만 바꿔주는 기능과 계산하는 기능이 따로 있는 것보단 같이 있는 게 좋을 것 같아 PostfixExpression라는 클래스로 다시 만들었습니다.
      • 해당 방식이 객체지향적으로 생각하는 것이 맞는 건지 궁금합니다.
      • 후위표기식으로 바꾸는 클래스와 계산하는 클래스로 또 나눠도 될 것 같긴 한데 멘토님의 생각이 궁금합니다!!

3. 입력값 검증

  • TDD로 구현하다보니 입력값에 대한 검증을 반복적으로 수행하는 경향이 있습니다.
    • 이런 경우 검증을 담당하는 클래스를 만들어 유틸성 클래스로 한 곳에서 처리하는 것이 좋을까요?
    • 저는 PostfixExpression가 생성될 때 검증을 통해 올바른 식만 보장하도록 만들 수 있도록 하였는 데 이 방식이 맞는 건지 궁금합니다.

Copy link

@epicblues epicblues left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[코드리뷰] 민성 -> 민환

Copy link

@bosuksh bosuksh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

민환님 과제하느라 고생 많으셨습니다.
확실히 퀄리티가 좋네요!
아까 리뷰시간에 리뷰하지 못한 부분에 대해서 코멘트도 달았습니다.
또한 질문 주신 부분에 답을 하자면

1. TDD 관련

해당 기능을 다른 클래스가 갖게 된다면 그에 해당하는 테스트코드도 그 클래스의 테스트 코드가 가져가야 하는게 당연하다고 생각합니다.
그리고 테스트코드 역시 관리의 대상이기 때문에 많으면 좋긴 하지만 그만큼 관리가 힘들어질 수 있습니다. 그러하기에 Private 메소드 테스트는 직접적으로 하는게 아닌 그 private 메소드를 사용하는 메소드의 테스트 케이스에서 커버를 해줘야 한다고 생각합니다. 그렇기에 private 메소드를 커버하는 테스트코드를 다양한 값을 통해 테스트해서 검증 할 필요가 있다고 생각합니다.

2. 클래스 분리를 명확하게 하지 못한 것 같습니다.

// 외부에서 OperatorType을 찾아 해당 Type으로 계산 실시
public static double calculate (double firstNumber, double secondNumber) {
    return expression.apply(firstNumber, secondNumber);
}

저는 개인적으로 후자가 맞다고 생각합니다. Operator을 생성하는거와 계산하는걸 분리시킬 필요가 있다고 생각하고 외부에서 생성한 객체로 단순히 계산만 진행하는게 더 좋을 것 같네요

후위 표기식을 계산하는 클래스와 바꾸는 클래스로 나누는 것이 좋다고 생각하네요
그런데 이걸 각각의 클래스로 만들어도 좋고, 아니면 inner class로 변환/ 계산 이렇게 생성해도 될 것 같네요.

3. 입력값 검증

Validation을 담당하는 클래스를 만들어서 하는건 좋은 방법이지만 유틸성으로 담기엔 검증대상이 너무 많기 때문에 각각 객체에 해당하는 Validator정도는 만드는게 좋다고 생각합니다.
어떤 객체가 조건에 만족하지 못하면 객체 생성을 막는것이 당연하기 때문에, 민환님께서 하신
PostfixExpression처럼 검증을 통해 생성을 막는것도 좋은 방식입니다.

@yuminhwan
Copy link
Author

yuminhwan commented Apr 2, 2022

안녕하세요 아만드님!
바쁘실텐데 좋은 리뷰 남겨주셔서 감사합니다!! 피드백 기반으로 리팩토링을 해보았습니다.
부족한 점이나 더 고쳐야할 점을 말씀해주시면 감사하겠습니다!!!
추가적으로 몇가지 질문이 있습니다.

질문

  1. 구글링을 통해 공부하다보면 메서드의 매개변수에 final키워드를 사용하는 것을 볼 수 있는 데 이경우에는 해당 매개변수가 불변임을 유지시켜주기 위하여 사용되어 지는 것일까요?? 현업에서도 사용되어지는 지 궁금합니다!
  2. 테스트 범위를 어디까지 해야할 지 명확하게 느껴지지 않습니다.
  • 단위테스트는 거의 다 작성했는 데 처리하지 못한 범위가 있을까봐(?) 불안합니다. 이 경우는 많이 작성해보는 것이 답일까요?
  • 현재는 Input,Output에 대한 테스트(Calculator쪽)는 진행하지 않았는 데 해당 클래스도 테스트를 진행해주는 것이 좋을까요?
  1. 커스텀 Exception으로도 구현해보려고 했는 데 OperatorType의 람다식에서는 throws를 할 수 없던데 이 경우는 어떻게 처리해야할까요?
  2. 현재는 Input쪽에서 검증을 해주고 있지 않은데 Input에서 어느정도의 검증을 실시해도 될까요? 예를 들면 메뉴 입력 시 숫자인지 아닌지를 체크하는 것들이요!

짧지만 2주라는 기간동안 많이 느끼고 배운 것 같습니다! 앞으로 팀이 어떻게 될지 모르지만 감사했습니다~!~!

@yuminhwan yuminhwan requested a review from bosuksh April 2, 2022 12:21
@learn-programmers learn-programmers merged commit f79a240 into prgrms-be-devcourse:yuminhwan Oct 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants