자바와 JUnit을 활용한 실용주의 단위 테스트 (8)

아연·2023년 11월 27일

Book

목록 보기
8/8
post-thumbnail

<자바와 JUnit을 활용한 실용주의 단위 테스트> 책과 Junit 5, AssertJ 공식문서를 참고하였습니다.



1. 메서드 추출

💡 세부 로직을 추출하여 메서드의 복잡성을 줄여라

Example

  • Profile.java (Before)
public boolean matches(Criteria criteria) {
  score = 0;
  
  boolean kill = false;
  boolean anyMatches = false;
  for (Criterion criterion: criteria) {
     Answer answer = answers.get(criterion.getAnswer().getQuestionText());
     boolean match = criterion.getWeight() == Weight.DontCare ||
           answer.match(criterion.getAnswer());

     if (!match && criterion.getWeight() == Weight.MustMatch) {
        kill = true;
     }
     if (match) {
        score += criterion.getWeight().getValue();
     }
     anyMatches |= match;
  }
  if (kill) return false;
  return anyMatches;
}
  • Profile.java (After)
public boolean matches(Criteria criteria) {
  score = 0;
  
  boolean kill = false;
  boolean anyMatches = false;
  for (Criterion criterion: criteria) {
     Answer answer = answers.get(criterion.getAnswer().getQuestionText());
     boolean match = matches(criterion, answer);

     if (!match && criterion.getWeight() == Weight.MustMatch) {
        kill = true;
     }
     if (match) {
        score += criterion.getWeight().getValue();
     }
     anyMatches |= match;
  }
  if (kill) return false;
  return anyMatches;
}

private boolean matches(Criterion criterion, Answer answer) {
  return criterion.getWeight() == Weight.DontCare ||
         answer.match(criterion.getAnswer());
}

고수준의 matches(Criteria criteria)
저수준의 matches(Criterion criterion, Answer answer)


ProfileCriteria 객체를 어떻게 매칭하는지에만 관심이 있다면?

→ 저수준의 matches 가 어떻게 매치되는지 신경 쓰지 않아도 된다 !
→ 복잡도가 줄었다 ! ദ്ദി˘•ω•˘ )


2. 메서드 이동

Example

위에서 메서드로 추출한 matches(Criterion criterion, Answer answer)Profile 객체와 관련이 없다. 그럼 파라미터인 CriterionAnswer 를 보자.

  • Answer.java
public class Answer {
   private int i;
   private Question question;
   // ... 
  • Criterion.java
public class Criterion implements Scoreable {
   private Weight weight;
   private Answer answer;
   private int score;
   // ... 

Criterion 이 멤버 변수로 Answer 를 가지고(알고) 있다. 즉, Criterion 객체는 Answer 객체를 의존한다. 이제 matches() 를 이동할 건데 Answer 에 이동하면 Criterion 과 Answer 객체는 양방향 의존관계가 되어 좋지 않다. 그러니 Criterion 객체로 이동시키자.

  • Criterion.java
public class Criterion implements Scoreable {
   private Weight weight;
   private Answer answer;
   private int score;
   // ... 
	 public boolean matches(Answer answer) {
      return getWeight() == Weight.DontCare || answer.match(getAnswer());
   }
}

이제 Profile 클래스도 수정하자.

  • Profile.java
public boolean matches(Criteria criteria) {
  score = 0;
  
  boolean kill = false;
  boolean anyMatches = false;
  for (Criterion criterion: criteria) {
     Answer answer = answers.get(criterion.getAnswer().getQuestionText());
		 boolean match = criterion.matches(answer);

     if (!match && criterion.getWeight() == Weight.MustMatch) {
        kill = true;
     }
     if (match) {
        score += criterion.getWeight().getValue();
     }
     anyMatches |= match;
  }
  if (kill) return false;
  return anyMatches;
}

리팩토링 후 복잡성도 줄었고 객체의 책임도 분명해졌다

0개의 댓글