Ch.4 설계 품질과 트레이드오프

Previously...

  • 객체지향 설계의 핵심 - 역할, 책임, 협력

    • 책임 주도 설계라는 이름에서 알 수 있듯 가장 중요한 것은 '책임'

  • 객체지향 설계 - 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동

    • 객체지향 설계에 관한 두 가지 관점

      1. 객체지향 설계의 핵심은 '책임'이다.

      2. 책임을 할당하는 작업이 응집도와 결합도 같은 설계 품질과 깊이 연관돼 있다.

    • 설계는 변경을 위해 존재하고, 변경에는 어떤 식으로든 비용이 발생한다. 따라서 합리적인 비용 안에서 변경을 수용할 수 있는(응집도가 높고 결합도가 느슨한) 구조가 유리하다.

      • 결합도와 응집도를 관리하기 위한 중요한 원칙 - 객체의 상태가 아닌 객체의 행동에 초점을 맞추는 것.

      • 그렇게 하지 않으면 객체의 내부 구현을 퍼블릭 인터페이스로 노출시키므로 변경에 취약해진다.

About

이번 챕터에서는 영화 예매 시스템을 데이터 주도적으로 설계해보고 객체지향적 구조와 어떤 차이점이 있는지 알아본다.

데이터 중심의 설계

  • 데이터 중심의 설계 - 객체 내부에 저장되는 데이터를 기반으로 시스템을 분할하는 방법.

  • 이 설계는 객체가 내부에 저장해야 하는 '데이터가 무엇인가'를 묻는 것으로 시작한다.

  • 캡슐화를 지키기 위해 접근자(accessor), 수정자(mutator)를 추가하는 방법이 있다.

  • 관찰해본 결과 메서드가 일련의 if-else, switch에 의존하는 경우가 많은 것 같다.

    • 객체가 어떤 데이터를 갖고 있는지에 따라 조건이 분기되고 그 분기마다 구현 사항을 다른 객체가 가져가는 것이 아니라 해당 객체가 고스란히 가져가기 때문으로 보인다.

단점

  • 캡슐화 위반

    • 객체의 내부 상태에 퍼블릭 인터페이스를 통해 노골적으로 접근하게 된다.

    • 협력에 관해 고민하지 않으면 캡슐화를 위반하는 과도한 접근자와 수정자를 갖게 되는 경향이 있다. 객체가 사용될 문맥을 추측할 수 밖에 없을 경우 해당 객체가 어떤 상황에서든 사용될 수 있게 최대한 많은 접근자 메서드를 추가하게 된다.

    • 이처럼 접근자와 수정자에 과도하게 의존하는 설계 방식을 추측에 의한 설계 전략이라고 부른다.

  • 높은 결합도

  • 낮은 응집

데이터 중심의 설계가 설계가 변경에 취약한 이유

  • 데이터 중심의 설계는 본질적으로 너무 이른 시기에 데이터에 관해 결정하도록 강요한다.

  • 데이터 중심의 설계에서는 협력이라는 문맥을 고려하지 않고 객체를 고립시킨 채 오퍼레이션을 결정한다.

객체의 '행동'보다는 '상태'에 초점을 맞춘다.

  • 접근자와 수정자는 public 속성과 큰 차이가 없으므로 캡슐화가 무너진다.

  • 데이터를 처리하는 작업과 데이터를 같은 객체 안에 두더라도 데이터에 초점이 맞춰져 있다면 만족스러운 캡슐화를 얻기 어렵다.

객체를 고립시킨 채 오퍼레이션을 결정하도록 만든다.

  • 올바른 객체지향 설계의 무게 중심은 항상 객체의 내부가 아닌 외부에 맞춰져 있어야 한다. 객체가 내부에 어떤 상태를 가지고 그 상태를 어떻게 관리할지는 부가적인 문제다. 중요한 것은 객체가 다른 객체와 협력하는 방법이다!

  • 데이터 중심 설계에서 초점은 객체의 외부가 아닌 내부로 향한다. 실행 문맥에 대한 깊은 고민이 없이 객체가 관리할 데이터의 세부 정보를 먼저 결정한다.

설계 트레이드오프

데이터 중심 설계와 책임 중심 설계의 장단점을 비교하기 위해 캡슐화, 응집도, 결합도를 사용해보자.

캡슐화, 설계의 첫 번째 목

  • 상태와 행동을 하나의 객체 안에 모으는 이유? 객체의 내부 구현을 외부로부터 감추기 위해서다. '캡슐화'

  • 객체지향에서 가장 중요한 원리 - 유연한 설계를 창조하기 위해 설계의 첫 번째 목표로 삼아야 한다.

  • 객체지향이 강력한 이유 - 한 곳에서 일어난 변경이 다른 곳에 영향을 끼치지 않도록 파급효과를 적절히 조절할 수 있기 때문

    • 구현 - 변경될 가능성이 높은 부분. 변경될 수 있는 어떤 것이든 캡슐화하자.

    • 인터페이스 - 상대적으로 안정적인 부분. 외부에는 인터페이스를 공개해 변경의 여파를 통제하자.

  • 속성의 가시성을 private하게 설정했다 하더라도 접근자와 수정자를 통해 속성을 외부로 제공하고 있다면 캡슐화를 위반하는 것이다.

  • 파라미터로 데이터를 받는 것 만으로도 캡슐화를 위반할 가능성이 있다.

    • e.g., 영화 예매 시스템 예시에서 isDiscountable(DayOfWeek dayOfWeek, LocalTime time) 메서드의 시그니처만으로 객체 내부에 DayOfWeek 타입의 요일과 LocalTime 타입의 시간 정보가 인스턴스 변수로 포함돼 있다는 사실을 인터페이스를 통해 외부로 노출하는 것이다.

  • 캡슐화를 통해 유지보수성을 높일 수 있다. 즉, 두려움 없이, 주저함 없이 코드를 변경할 수 있는 힘을 얻게 된다.

캡슐화란, 변경 가능성이 높은 부분을 객체 내부로 숨기는 추상화 기법이다. 변하는 어떤 것이든 감춰야 한다.

응집도와 결합도

  • 응집도 - 모듈에 포함된 내부 요소들이 연관돼 있는 정도.

    • 모듈 내 요소들이 하나의 목적을 위해 긴밀하게 협력하는 것이 좋다. 관련도가 높은 책임을 객체와 클래스에 할당하는 척도이다.

    • 변경이 발생할 때 모듈 내부에서 발생하는 변경의 정도.

  • 결합도 - 의존성의 정도를 나타내며, 다른 모듈에 대해 얼마나 많은 지식을 갖고 있는지를 나타내는 척도.

    • 어떤 모듈이 다른 모듈에 대해 너무 자세한 부분(내부 구현)까지 알고 있다면 높은 결합도를 갖게 된다.

    • 한 모듈이 변경되기 위해서 다른 모듈의 변경을 요구하는 정도.

  • 얼마나 강하게 연관돼 있어야 응집도가 높다고 할 수 있고, 어느 정도의 의존성만 남겨야 결합도가 낮다고 할 수 있을까?

    • 어쨌든 이 둘은 좋은 설계를 만드는 데에 영향을 미친다. 좋은 설계는 변경과 관련된 것이고 응집도와 결합도의 정도가 설계의 품질을 결정하므로, 이 둘은 변경과 관련돼 있다.

  • 결합도가 높아도 괜찮을 때도 있다. 일반적으로 변경될 확률이 매우 낮은 안정적인 모듈에 의존하는 것은 문제가 되지 않는다.

    • e.g., 자바의 String, ArrayList

    • 그러나 직접 작정한 코드의 경우는 해당 사항이 아니다.

스스로 자신의 데이터를 책임지는 객체

객체를 설계할 때 "이 객체가 어떤 데이터를 포함해야 하는가?"라는 질문은 다음 두 개의 개별적인 질문으로 분리되어야 한다.

  1. 이 객체가 어떤 데이터를 포함해야 하는가?

  2. 이 객체가 데이터에 대해 수행하는 오퍼레이션은 무엇인가?

이 두 질문을 조합하면 객체의 내부 상태를 저장하는 방식과 저장된 상태에 대해 호출할 수 있는 오퍼레이션의 집합을 얻을 수 있다. 다시 말해 새로운 데이터 타입을 만들 수 있다.

결론

이번 챕터는 데이터 주도 설계의 문제를 알아보면서 오히려 책임 주도 설계의 장점을 부각시켰다.

여태까지 객체 내부에 어떤 걸 저장할 지를 먼저 고민하는 데이터 주도 설계, 그리고 미리 어떤 행동이 필요할 지 고려하지 않고 내부 상태부터 만들어 놓고 개발하는 추측에 의한 설계를 했던 것 같다.

이번 챕터를 읽어보니 좋은 설계를 어떻게 할지 실질적으로 감이 오는 것 같다.

스터디

Last updated