Eternity's Chit-Chat

aeternum.egloos.com



유연한 설계를 위한 패턴과 원리 - 4.잃어버린 시간을 찾아서 3부 Supple Design

TimeOfDay는 하루 중 특정 시간을 나타내며 밀리초 단위까지 표현할 수 있다. TimeOfDay에서 분, 초, 밀리초를 생략할 수 있도록 메소드를 오버로딩한 부분을 눈여겨 보기 바란다. TimeOfDdy 자체에서 불필요한 파라미터를 생략 가능하도록 CREATION METHOD를 제공하기 때문에 이를 사용하는 TimePoint에서는 분, 초, 밀리초를 생략하기 위한 메소드를 오버로딩할 필요가 없이 TimeOfDay를 파라미터로 사용하기만 하면 된다.

TimeOfDay.java
public class TimeOfDay {
  private int hour;
  private int minute;
  private int second;
  private int milisecond;

 
public static TimeOfDay atMidnight() {
   
return at(0);
 
}

 
public static TimeOfDay atNoon() {
   
return at(12);
 
}

 
public static TimeOfDay at(int hour) {
   
return at(hour, 0);
 
}

 
public static TimeOfDay at(int hour, int minute) {
    return at(hour, minute, 0, 0);
  }

 
public static TimeOfDay at(int hour, int minute, int second) {
    return new TimeOfDay(hour, minute, second, 0);
  }

  public
static TimeOfDay at(int hour, int minute, int second, int milisecond) {
    return new TimeOfDay(hour, minute, second, milisecond);
  }

  private
TimeOfDay(int hour, int minute, int second, int milisecond) {
    this.hour = hour;
    this.minute = minute;
    this.second = second;
    this.milisecond = milisecond;
  }

 
int getHour() {
    return hour;
  }

 
int getMinute() {
   
return minute;
 
}

 
int getSecond() {
   
return second;
 
}

 
int getMilisecond() {
   
return milisecond;
 
}

TimeOfDay 역시 Comparable 인터페이스를 구현하고 isBefore(), isAfter(), equals()와 hashCode() 메소드를 오버라이딩한다.

TimeOfDay.java
public class TimeOfDay implements Comparable<TimeOfDay> {
  ......
 
public boolean isBefore(TimeOfDay other) {
   return compareTo(other) < 0;
  }

 
public boolean isAfter(TimeOfDay other) {
   return compareTo(other) > 0;
  }

 
public int compareTo(TimeOfDay other) {
    if (hour < other.hour) {
      return -1;
    }

    if (hour > other.hour) {
      return 1;
    }

    if
(minute < other.minute) {
      return -1;
    }

    if
(minute > other.minute) {
      return 1;
    }

    if
(second < other.second) {
      return -1;
    }

    if
(second > other.second) {
      return 1;
    }

    return 0;
  }
}

이제 TimePoint에 INTRODUCE PARAMETER OBJECT 리팩토링을 적용하자. TimePoint에 DayOfYear, TimeOfDay, TimeZone을 파라미터로 받는 at() CREATION METHOD를 추가한다. 새로운 at() 메소드는 8개의 파라미터를 취하는 at() 메소드를 사용해서 TimePoint를 생성한다.

TimePoint.java
public static TimePoint at(DayOfYear dayOfYear, TimeOfDay timeOfDay,

TimeZone zone) {

return at(dayOfYear.getYear(), dayOfYear.getMonth(), dayOfYear.getDay(),

timeOfDay.getHour(), timeOfDay.getMinute(), timeOfDay.getSecond(),

timeOfDay.getMilisecond(), zone);

}

클라이언트가 긴 파라미터를 취하는 at() 메소드를 사용할 수 없도록 가시성을 패키지 내부로 제한하여 전체 파라미터가 필요한 경우에는 DayOfYear와 TimeOfDay만을 사용하도록 강제하자. 이 때 연, 월, 일, 시, 분의 5개 파라미터를 취하는 at() 메소드는 그대로 public 가시성을 유지하고 있음에 주목하자. 이것은 분까지를 파라미터로 취하는 메소드를 외부에 제공하는 것이 사용성이나 가독성을 향상시킨다고 판단했기 때문이다.

TimePoint.java

public static TimePoint at(int year, int month, int day,
  int hour, int minute, TimeZone zone) {
  return at(year, month, day, hour, minute, 0, 0, zone);
}

static
TimePoint at(int year, int month, int day, int hour,
  int minute, int second, int millisecond, TimeZone zone) {
  Calendar calendar = Calendar.getInstance(zone);
  calendar.set(Calendar.YEAR, year);
  calendar.set(Calendar.MONTH, month - 1);
  calendar.set(Calendar.DATE, day);
  calendar.set(Calendar.HOUR_OF_DAY, hour);
  calendar.set(Calendar.MINUTE, minute);
  calendar.set(Calendar.SECOND, second);
  calendar.set(Calendar.MILLISECOND, millisecond);
  return new TimePoint(calendar.getTime().getTime());
}

PATTERNS & PRINCIPLES
대칭성(Symmetric)

자연이 아름다운 이유는 형태와 행위에 있어 대칭성이 존재하기 때문이다. 대칭성은 안정감을 느끼게 해준다. 비대칭적인 형태를 보고 있으면 심리적으로 불안감을 느끼게 된다. 대칭성은 예측 가능성과도 연결된다. 하나의 측면을 이해하는 것만으로도 다른 측면을 예상할 수 있다.
소프트웨어를 아름답게 만들기 위해서는 자연의 대칭성을 코드 내부로 소환해야 한다. 코드에 존재하는 대칭성을 명확하게 표현하면 코드가 깔끔해지고 이해하기 쉬워진다. 도메인에 존재하는 대칭성을 찾아 설계에 반영하면 전체적으로 통일성을 보장할 수 있다. 여기에서의 통일성은 하나의 아이디어를 프로그램 전반에 걸쳐 일관되게 사용하는 것을 의미한다. 대칭성을 만족시키는 코드는 읽기 편하고 이해하기 쉽다.
코드의 대칭성은 모든 수준에서 그 위력을 발휘한다. 코드에서 add()라는 메소드가 존재한다면 대부분의 사람들은 요소를 삭제하기 위해 remove()라는 메소드를 연상할 것이다. 그렇다면 remove()를 추가하라. 날짜를 의미하는 DayOfYear가 존재한다면 시간을 의미하는 TimeOfDay도 기대할 것이다. 그 기대를 충족시켜 줘라. DayOfYear에 isBefore()가 존재한다면 isAfter()도 존재하는지 물어 볼 것이다. 그 질문에 긍정적인 답변을 해주어라. DayOfYear에 isBefore()와 isAfter()가 존재한다면 TimeOfDay에도 두 메소드가 존재해야 균형이 맞지 않을까? 개념의 추가 한 쪽으로 기울어지지 않도록 하라.
대칭성은 자기 유사성을 의미한다. 코드의 많은 부분이 자기 유사성을 띠면 띨수록 코드를 이해하는데 필요한 개념의 무게가 줄어든다.