테스트 코드 실습 (학점 계산기 구현)

Jiruen·2023년 5월 25일
0

FastCampus-Spring study

목록 보기
4/4

🌁 요구사항

  1. 평균 학점 계산 방법
    = (학점 수 x 교과목 평점)의 합계 / 수강 신청 총 학점 수
  2. 일급 컬렉션 사용

📂 학점 계산기 도메인

  • 이수한 과목( 객체지향프로그래밍, 자료구조, 중국어회화 )
  • 학점 계산기

🔗 흐름도

  1. 이수한 과목을 전달하여 평균학점 계산 요청 ---> 학점 계산기
  2. 학점 계산기 ---> (학점 수 x 교과목 평점)의 합계 / 수강신청 총 학점 수
  3. (학점 수 x 교과목 평점)의 합계 & 수강신청 총 학점 수 ---> 과목(코스)

Course.java

  • Field
    • String subject // 과목명
    • int credit // 학점
    • String grade // 성적 (A+, A, B+, .... )
  • Method
    • int getCredit() // 학점 반환
    • double getGradeToNumber() // 문자인 성적을 double로 변환하여 반환
public class Course {
    private final String subject; 
    private final int credit; 
    private final String grade; 

    public Course(String subject, int credit, String grade) {
        this.subject = subject;
        this.credit = credit;
        this.grade = grade;
    }

    public int getCredit() {
        return credit;
    }

    public double getGradeToNumber() {
        double gradeInt = 0;
        switch (this.grade) {
            case "A+":
                gradeInt = 4.5;
                break;
            case "A":
                gradeInt = 4.0;
                break;
            case "B+":
                gradeInt = 3.5;
                break;
            case "B":
                gradeInt = 3.0;
                break;
            case "C+":
                gradeInt = 2.5;
                break;
            case "C":
                gradeInt = 2.0;
                break;
            case "D+":
                gradeInt = 1.5;
                break;
            case "D":
                gradeInt = 1.0;
                break;
            case "F":
                gradeInt = 0.0;
                break;
        }
        return gradeInt;
    }
}

CourseTest.java

public class CourseTest {
    @DisplayName("과목(코스)를 생성한다.")
    @Test
    void createTest() {
        assertThatCode(()-> new Course("OOP", 3, "A")).doesNotThrowAnyException();
    }
}

GradeCalculator.java

  • Field
    • List< Course > coures // 이수 과목명 List
  • Method
    • double calculateGrade() // 이수 과목들의 학점 구하기
public class GradeCalculator {
    private final List<Course> coures;
    public GradeCalculator(List<Course> courses) {
        this.coures = courses;
    }
    
    // 전달 받은 이수 과목들의 학점 구하기
    public double calculateGrade() {
        // (학점 수 x 교과목 평점)의 합계
        double multiplideCreditAndCourseGrade = 0;
        for( Course course : coures){
            multiplideCreditAndCourseGrade += course.getCredit() * course.getGradeToNumber();
        }

        // 수강신청 총학점 수
        int totalCompletedCredit = coures.stream()
                .mapToInt(Course::getCredit)
                .sum();
        return multiplideCreditAndCourseGrade / totalCompletedCredit;
    }
}

GradeCalculatorTest.java

  • GradeCalculator gradeCalculator = new GradeCalculator(courses);
    • 이수한 과목 전달
  • double gradeResult = gradeCalculator.calculateGrade();
    • 이수한 과목들의 학점 계산
  • assertThat(gradeResult).isEqualTo(4.5);
    • 이수한 과목들의 학점이 4.5가 맞는지 확인
public class GradeCalculatorTest {
    @DisplayName("평균 학점을 계산한다.")
    @Test
    void calculateGradeTest() {
        List<Course> courses = List.of(new Course("OOP", 3, "A+"),
                new Course("자료구조", 3, "A+"));

        GradeCalculator gradeCalculator = new GradeCalculator(courses); // 이수한 과목 전달
        double gradeResult = gradeCalculator.calculateGrade(); // 이수한 과목의 성적 계산
        assertThat(gradeResult).isEqualTo(4.5);
    }
}

⛔ BUT 문제 발생 ⛔

  • Course는 학점성적이 변수로 선언

    public class Course {
       private final String subject; 
       private final int credit; 
       private final String grade; 
       ...
    }
  • (학점 수 x 교과목 평점)의 합계를 course가 계산하는 것이 아니라 getter로 가져와서 GradeCalculator에서 수행

    public class GradeCalculator {
       ...
       public double calculateGrade() {
           // (학점 수 x 교과목 평점)의 합계
           double multiplideCreditAndCourseGrade = 0;
           for( Course course : coures){
               multiplideCreditAndCourseGrade += course.getCredit() * course.getGradeToNumber();
           }
    
           // 수강신청 총학점 수
           int totalCompletedCredit = coures.stream()
                   .mapToInt(Course::getCredit)
                   .sum();
           return multiplideCreditAndCourseGrade / totalCompletedCredit;
       }
    }

📣 (학점 수 x 교과목 평점)의 합계가 여러 군데에서 사용 시에는?

  • 해당 로직이 변경되면 여러 군데 모두 수정 필요
    -> 응집도가 약하다!
        // (학점 수 x 교과목 평점)의 합계
        double multiplideCreditAndCourseGrade = 0;
        for( Course course : coures){
            multiplideCreditAndCourseGrade += course.getCredit() * course.getGradeToNumber();
        }

💡 해결 방법

  • getter 대신 (학점 수 x 교과목 평점)의 합계를 Course에서 학점 계산 수행
  • 해당 객체에게 작업 위임!
  • Course.java
      public double multiplyCreditAndGrade() {
          return credit * getGradeToNumber();
      }
  • GradeCalculator.java
    public double calculateGrade() {
           double multiplideCreditAndCourseGrade = 0;
           for( Course course : coures){
               multiplideCreditAndCourseGrade += course.multiplyCreditAndGrade();
           }
           ...
    }
    • 기존 getter 방식에는 사용하는 곳곳마다 찾아가서 수정 요구
    • 정보를 가진 객체에게 메세지를 전달하여 해당 객체에서 수행한다면 해당 한 부분만 수정해주면 된다!
      -> 응집도가 높으므로 변화가 있을때 한군데만 수정하면 ok

📌 정리

  • getter로 가져와서 정보를 처리하는 방법 대신 해당 데이터를 가진 객체에게 메시지를 던져서(메소드) 작업을 시켜주는 것이 변화에 유연하다!

📂 일급 컬렉션 사용

  • 아직까지도 이 부분이 GradeCalculator에 존재!

    public double calculateGrade() {
          // (학점 수 x 교과목 평점)의 합계
          double multiplideCreditAndCourseGrade = 0;
          for( Course course : coures){
              multiplideCreditAndCourseGrade += course.multiplyCreditAndGrade();
          }
         ...
    }
  • Courses.java

    public class Courses {
       private final List<Course> courses;
    
       public Courses(List<Course> courses) {
           this.courses = courses;
       }
    
       public double multipyCreditAndCourseGrade() {
           return courses.stream()
                   .mapToDouble(Course::multiplyCreditAndGrade)
                   .sum();
       }
    
       public int calculateTotalCompletedCredit() {
           return courses.stream()
                   .mapToInt(Course::getCredit)
                   .sum();
       }
    }
  • GradeCalculator.java

    • private final List courses; // Courses로 이동

    • private final Courses courses; // Field에 Courses 객체 선언

      public class GradeCalculator {
         private final Courses courses;
         
         public GradeCalculator(List<Course> courses) {
             this.courses = new Courses(courses);
         }
      
         //전달 받은 이수 과목들의 (학점 수 x 교과목 평점)의 합계 구하기
         public double calculateGrade() {
             // (학점 수 x 교과목 평점)의 합계
             double totalMultipliedCreditAndCourseGrade = courses.multipyCreditAndCourseGrade();
      
             // 수강신청 총학점 수
             int totalCompletedCredit = courses.calculateTotalCompletedCredit();
      
             return totalMultipliedCreditAndCourseGrade / totalCompletedCredit;
         }
      }
    • GradeCalculator에 있던 정보가 multipyCreditAndCourseGrade()이라는 Courses로 이동

일급 컬렉션이란?

  • Course들을 여러개 가진 정보만을 인스턴스로 가지는 클래스
  • 해당 정보만 가진 / 여러 개의 코스 정보만을 가진 클래스 (ex 리스트 형태)
  • 이 정보를 가지고 처리할 수 있는 책임들이 해당 일급 컬렉션 밑으로 모두 이동!
  • 수정이 들어오면 해당 메소드만 수정!

핵심 포인트

  1. 이수한 과목을 전달하여 평균학점 계산 요청 ---> 학점 계산기

    • 이수한 과목을 전달하여 평균학점 계산 요청
      GradeCalculator gradeCalculator = new GradeCalculator(courses); // 이수한 과목 전달
      double gradeResult = gradeCalculator.calculateGrade(); // 이수한 과목의 성적 계산
  2. 학점 계산기 ---> (학점 수 x 교과목 평점)의 합계 ---> 과목(코스)

    • 학점 계산기 ---> (학점 수 x 교과목 평점)의 합계 ---> 과목(코스)

      public double calculateGrade() {
            // (학점 수 x 교과목 평점)의 합계
            double totalMultipliedCreditAndCourseGrade = courses.multipyCreditAndCourseGrade();
      
            // 수강신청 총학점 수
            int totalCompletedCredit = courses.calculateTotalCompletedCredit();
      
            return totalMultipliedCreditAndCourseGrade / totalCompletedCredit;
         }
    • 학점 수 교과목 평균의 합계를 구하기 위해 일급 컬렉션에게 메소드 전달하고 값을 받아온다!

    • 과목 (코스) 일급 컬렉션에게 작업을 위임해서 값을 받아옴

0개의 댓글