좋은 설계란? (feat. 결합도, 응집도)

bp.chys·2020년 3월 19일
1

OOP & Design Pattern

목록 보기
3/17

좋은 설계

  • 좋은 설계란 오늘 요구하는 기능을 온전히 수행하면서, 내일의 변경을 매끄럽게 수용할 수 있는 설계를 가리킨다.
  • 객체 지향 프로그래밍은 객체 사이 의존성을 효율적으로 통제할 수 있는 다양한 방법을 제공하면서 좋은 설계를 도와주는 좋은 도구이다.
    • 객체 사이의 의존성이란 객체들이 협력하는 과정 속에서 객체들은 다른 객체에 의존하게 되는 것이다.
  • 보다 구체적인 의미로, 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동을 가리킨다.

결합도와 응집도

응집도

  • 응집도는 모듈에 포함된 내부 요소들이 연관돼 있는 정도를 나타낸다.
  • 모듈 내의 요소들이 하나의 목적을 위해 긴밀하게 협력한다면 그 모듈은 높은 응집도를 가진다.
  • 모듈 내의 요소들이 서로 다른 목적을 추구한다면 그 모듈은 낮은 응집도를 가진다.
  • 객체지향 관점에서 응집도는 객체 또는 클래스에 얼마나 관련 높은 책임들을 할당했는지를 나타낸다.

낮은 응집도 예시 😭

  • 할인 조건의 종류를 변경하기 위해서는 DiscountCondition, Movie, 그리고 Movie를 사용하는 Screening까지 함께 수정해야한다.
  • 하나의 변경을 수용하기 위해 코드의 여러 곳을 동시에 변경해야 한다는 것은 설계의 응집도가 낮다는 것을 의미한다.
public class Screening {
    public Money calculateFee(int audienceCount) {
        switch (movie.getMovieType()) {
            case AMOUNT_DISCOUNT:
                if (movie.isDiscountable(whenScreened, sequence) {
                    return movie.calculateAmountDiscountedFee().times(audienceCount);
                }
                break;
            case PERCENT_DISCOUNT:
                if (movie.isDiscountable(whenScreened, sequence)) {
                    return movie.calculatePercentDiscountedFee().times(audienceCount);
                }
                break;
            case NONE_DISCOUNT:
                return movie.calculateNoneDiscountedFee().times(audienceCount);
        }
        return movie.calculatedNoneDiscountedFee().times(audienceCount);
    }
}

결합도

  • 결합도는 의존성의 정도를 나타내며 다른 모듈에 대해 얼마나 많은 지식을 갖고 있는지를 나타낸다.
  • 어떤 모듈이 다른 모듈에 대해 자세한 부분까지 알고 있다면 두 모듈은 높은 결합도를 가진다.
  • 어떤 모듈이 다른 모듈에 대해 꼭 필요한 지식만 알고 있다면 두 모듈은 낮은 결합도를 가진다.
  • 결합도는 협력에 필요한 적절한 수준의 관계만을 유지하는 것이 중요하다.
  • 결합도는 한 모듈이 변경되기 위해서 다른 모듈의 변경을 요구하는 정도로 측정할 수 있다.
    • 내부 구현을 변경했을 때 이것이 다른 모듈에 영향을 미치는 경우 → 높은 결합도
    • 퍼블릭 인터페이스를 수정했을 때만 다른 모듈에 영향을 미치는 경우 → 낮은 결합도
    • 따라서 구현이 아닌 인터페이스에 의존하도록 코드를 작성해야 낮은 결합도를 얻을 수 있다.

높은 결합도 예시 😭

  • DiscountCondition의 기간 할인 조건의 명칭이 PERIOD에서 다른 값으로 변경된다면 Movie를 수정해야 한다.
  • DiscountCondition의 종류가 추가되거나 삭제된다면 Movie안의 if ~ else 구문을 수정해야 한다.
  • 각 DiscountCondition의 만족 여부를 판단하는 데 필요한 정보가 변경된다면 Movie의 isDiscountable 메서드로 전달된 파라미터를 변경해야 한다. 이로 인해 Movie의 isDiscountable 메서드 시그니처도 함께 변경될 것이고 결과적으로 이 메서드에 의존하는 Screening에 대한 변경을 초래할 것이다.
public class Movie {
    public boolean isDiscountable(LocalDateTime whenScreened, int sequence) {
    	for (DiscountCondition condition : discountConditions) {
            if (condition.getType() == DiscountConditionType.PERIOD) {
            	if (condition.isDiscountable(whenScreened.getDayOfWeek(), whenScreened.toLocalTime())) {
                    return true;
                }
            } else {
                if (condition.isDiscountable(sequence)) {
                    return true;
                }
            }
        }
        return false;
    }
}

결론

좋은 설계는 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도높은 응집도를 지향하는 것이다. 조금 더 단순하게 말하면, 객체가 변경되는 이유는 한 가지 뿐이어야 하며, 그 변경의 여파가 다른 객체로 전파되지 않아야 한다.
이를 효과적으로 지원해주는 방법은 바로 캡슐화이다. 캡슐화에 대해서는 다음 포스팅에서 자세히 알아보겠다.

지금까지 내용을 간단히 정리해보면, 객체는 행위 기반의 책임을 바탕으로 설계되어야하며, 낮은 결합도와 높은 응집도를 지향하기 위해서는 단일 책임을 가져야하며 이를 지원해주는 매커니즘은 캡슐화이다.


참고자료

  • NHN 기술세미나, 객체지향 입문 - 최범균
  • 오브젝트 - 조영호
profile
하루에 한걸음씩, 꾸준히

0개의 댓글