객체지향 설계 기법(책임-주도 설계, 디자인 패턴, 테스트-주도 개발)
객체지향 설계 기법
역할, 책임, 협력의 관점에서 애플리케이션을 설계하는 유용한 세 가지 기법
1. 책임-주도 설계
객체지향 시스템의 목적은 사용자의 요구를 만족시킬 수 있는 기능을 제공하는 동시에 이해하기 쉽고, 단순하며, 유연한 상호작용을 제공하는 객체들의 공동체를 구축하는 것이다.
결국 객체지향 설계란 애플리케이션의 기능을 구현하기 위한 협력 관계를 고안하고, 협력에 필요한 역할과 책임을 식별한 후 이를 수행할 수 있는 적절한 객체를 식별해 나가는 과정이다.
객체지향 설계의 핵심은 올바른 책임을 올바른 객체에게 할당하는 것이다.
책임-주도 설계 방법은 현재 가장 널리 받아들여지는 객체지향 설계 방법이다.
- 시스템의 기능은 더 작은 규모의 책임으로 분할되고 각 책임은 책임을 수행할 적절한 객체에게 할당된다.
- 객체가 책임을 수행하는 도중에 스스로 처리할 수 없는 정보나 기능이 필요한 경우 적절한 객체를 찾아 필요한 작업을 요청한다.
- 요청된 작업을 수행하는 일은 이제 작업을 위임받은 객체의 책임으로 변환된다.
- 객체가 다른 객체에게 작업을 요청하는 행위를 통해 결과적으로 객체들 간의 협력 관계가 만들어진다.
- 만약 책임을 여러 종류의 객체가 수행할 수 있다면 협력자는 객체가 아니라 추상적인 역할로 대체된다.
이처럼 책임-주도 설계에서는 시스템의 책임을 객체의 책임으로 변환하고, 각 객체가 책임을 수행하는 중에 필요한 정보나 서비스를 제공해줄 협력자를 찾아 해당 협력자에게 책임을 할당하는 순차적인 방식으로 객체들의 협력 공동체를 구축한다.
결과적으로 시스템은 스스로 자신을 책임질 수 있을 정도로 충분히 자율적인 동시에 다른 객체와 우호적으로 협력할 수 있을 정도로 충분히 협조적인 객체들로 이뤄진 생태계를 구성하게 된다.
핵심 요약
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악한다.
- 시스템 책임을 더 작은 책임으로 분할한다.
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당한다.
- 객체가 책임을 수행하는 중에 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다.
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 한다.
2. 디자인 패턴
디자인 패턴은 책임-주도 설계의 결과를 표현한다.
패턴은 모범이 되는 설계다.
패턴은 특정한 상황에서 설계를 돕기 위해 모방하고 수정할 수 있는 과거의 설계 경험이다.
일반적으로 디자인 패턴은 반복적으로 발생하는 문제와 그 문제에 대한 해법의 쌍으로 정의된다.
- 패턴은 해결하려고 하는 문제가 무엇인지를 명확하게 서술하고, 패턴을 적용할 수 있는 상황과 적용할 수 없는 상황을 함께 설명한다.
- 패턴은 반복해서 일어나는 특정한 상황에서 어떤 설계가 왜 더 효과적인지에 대한 이유를 설명한다.
예) COMPOSITE 패턴
COMPOSITE 패턴은 전체와 부분을 하나의 단위로 추상화해야 하는 경우에 사용할 수 있는 패턴이다.
COMPOSITE 패턴은 클라이언트 입장에서 메시지 수신자가 부분인지 전체인지에 상관 없이 동일한 메시지를 이용해 동일한 방식으로 대상과 상호작용하고 싶을 때 사용할 수 있는 패턴이다.
- Component는 클라이언트와 협력할 수 있는 공용 인터페이스를 정의하는 역할을 수행한다.
- Leaf 역할은 공용 인터페이스에 대한 오퍼레이션 호출에 응답할 수 있는 기본적인 행위를 구현한다.
- Composite은 외부로부터 부분에 대한 세부 사항을 감추고 포함된 부분을 하나의 단위로 행동하는 역할이다.
Composite 패턴은 부분과 전체가 투명하고 동일한 인터페이스를 제공해야 한다는 제약하에서 식별된 역할, 책임, 협력을 제공하는 한 가지 설계 예제다.
따라서 디자인 패턴은 유사한 상황에서 반복적으로 적용할 수 있는 책임-주도 설계의 결과물이라고 할 수 있다.
디자인 패턴은 공통으로 사용할 수 있는 역할, 책임, 협력의 템플릿이다. 만약 특정한 상황에 적용 가능한 디자인 패턴을 잘 알고 있다면 책임-주도 설계의 절차를 순차적으로 따르지 않고도 시스템 안에 구현할 객체들의 역할과 책임, 협력 관계를 손쉽게 포착할 수 있을 것이다.
디자인 패턴은 책임-주도 설계의 결과물인 동시에 지름길이다.
3. 테스트-주도 개발
테스트-주도 개발은 애자일 방법론의 한 종류인 XP의 기본 프랙스티로 소개되면서 주목받기 시작한 설계 기법이다.
테스트-주도 개발의 기본 흐름은 실패하는 테스트를 작성하고, 테스트를 통과하는 가장 간단한 코드를 작성한 후, 리팩터링을 통해 중복을 제거하는 것이다.
테스트-주도 개발을 통해 ‘작동하는 깔끔한 코드’를 얻을 수 있다.
- 테스트-주도 개발은 객체가 이미 존재한다고 가정하고 객체에게 어떤 메시지를 전송할 것인지에 관해 먼저 생각하라고 충고한다.
- 테스트를 작성하는 것이 아니라 책임을 수행할 객체 또는 클라이언트가 기대하는 객체의 역할이 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 기대를 코드의 형태로 작성하는 것이다.
- 협력 안에서 역할과 책임이 무엇이고 이것이 클래스와 같은 프로그래밍 언어 장치로 구현되는 방식에 대한 감각을 갖춰야만 효과적인 테스트를 작성할 수 있다.
테스트-주도 개발은 객체지향에 대한 깊이 있는 지식을 요구한다.
- 테스트를 작성하기 위해 객체의 메서드를 호출하고 반환값을 검증하는 것은 순차적으로 객체가 수행해야 하는 책임에 관해 생각한 것이다.
- 테스트에 필요한 간접 입력 값을 제공하기 위해 스텁(stub)을 추가하거나 간접 출력 값을 검증하기 위해 목 객체(mock)를 사용하는 것은 객체와 협력해야 하는 협력자에 관해 고민한 결과를 코드로 표현한 것이다.
역할, 책임, 협력에 집중하고 객체지향의 원칙을 적용하려는 깊이 있는 고민과 노력을 통해서만 테스트-주도 개발의 혜택을 누릴 수 있다.