개요
이번장에서는 유연하고 재사용 가능한 퍼블릭 인터페이스를 만드는 데 도움이 되는 설계 원칙과 기법을 익히기 위한 개념들을 살펴봅니다.
메시지(message)
- 객체가 다른 객체와 협력하기 위해 사용하는 의사소통 메커니즘을 메시지(message), 일반적으로 객체의 오퍼레이션이 실행되도록 요청하는 것을 메시지 전송(message sending)이라고 부릅니다.
- 메시지는 협력에 참여하는 전송자와 수신자 양쪽 모두를 포함하는 개념입니다.
- 메시지는 오퍼레이션명과 인자로 구성되며 메시지 전송은 여기에 메시지 수신자를 추가한 것입니다.
- 따라서 메시지 전송은 메시지 수신자, 오퍼레이션명, 인자의 조합입니다.
퍼블릭 인터페이스
- 객체가 의사소통을 위해 외부에 공개하는 메시지의 집합을 퍼블릭 인터페이스라고 부릅니다.
- 클래스의 퍼블릭 메서드들의 집합이나 메시지의 집합을 가리키는 데 사용됩니다.
오퍼레이션(operation)
- 객체가 다른 객체에게 제공하는 추상적인 서비스(인터페이스)를 오퍼레이션이라고 부릅니다.
- 오퍼레이션은 메시지 전송자는 고려하지 않은 채 메시지 수신자의 관점만을 다룹니다.(객체의 인터페이스를 강조)
- 따라서 메시지 수신이란 메시지에 대응되는 객체의 오퍼레이션을 호출하는 것을 의미합니다.
메서드
- 메시지에 응답하기 위해 실행되는 코드 블록을 메서드라고 부릅니다.
- 메서드는 오퍼레이션의 구현입니다.
- 메시지를 수신했을 때 실제로 어떤 코드가 실행되는지는 메시지 수신자의 실제 타입이 무엇인가에 달려 있습니다.
- 실행 시점에 메시지와 메서드를 바인딩하는 메커니즘은 두 객체 사이의 결합도를 낮춤으로써 유연하고 확장 가능한 코드를 작성할 수 있게 만들 수 있게 합니다.
- 오퍼레이션과 메서드의 구분은 다형성의 개념과 연결이 됩니다.
시그니처(signature)
- 오퍼레이션(또는 메서드)의 이름과 파라미터 목록을 합쳐 시그니처라고 부릅니다.
- 오퍼레이션은 실행 코드 없이 시그니처만을 정의한 것이며 메서드는 이 시그니처에 구현을 더한 것입니다.
- 일반적으로 메시지를 수신하면 오퍼레이션의 시그니처와 동일한 메서드가 실행됩니다.
- 오퍼레이션의 관점에서 다형성이란 동일한 오퍼레이션 호출에 대해 서로 다른 메서드들이 실행되는 것이라고 정의합니다.
좋은 인터페이스 설계란 무엇일까?
- 좋은 인터페이스는 최소한의 인터페이스와 추상적인 인터페이스라는 조건을 만족해야 합니다.(3장 참고)
- 최소주의를 따르면서도 추상적인 인터페이스를 설계할 수 있는 가장 좋은 방법은 책임 주도 설계 방법을 따르는 것입니다.
- 책임 주도 설계 방법은 메시지를 먼저 선택함으로써 협력과는 무관한 오퍼레이션이 인터페이스에 스며드는 것을 방지합니다.
- 따라서 최소의 오퍼레이션만 포함하게 됩니다.
- 또한 객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 함으로써 클라이언트의 의도를 메시지에 표현할 수 있게 합니다.
- 퍼블릭 인터페이스의 품질에 영향을 미치는 다음과 같은 원칙과 기법들이 존재합니다.
디미터 법칙(Law of Demeter)
- 협력하는 객체의 내부 구조에 대한 결합으로 인해 발생하는 설계 문제를 해결하기 위해 제안된 원칙입니다.
디미터 법칙을 어떻게 적용해야 할까?
- 클래스 내부의 메서드가 아래 조건을 만족하는 인스턴스에만 메시지를 전송하도록 프로그래밍해야 합니다.
- this 객체
- 메서드의 매개변수
- this의 속성
- this의 속성인 컬렉션의 요소
- 메서드 내에서 생성된 지역 객체
- 디미터 법칙을 따르면 부끄럼 타는 코드를 작성할 수 있습니다.
- 디미터 법칙을 따르는 코드는 메시지 수신자의 내부 구조가 전송자에게 노출되지 않으며, 메시지 전송자는 수신자의 내부 구현에 결합되지 않습니다.
- 따라서 클라이언트와 서버 사이에 낮은 결합도를 유지할 수 있습니다.
부끄럼 타는 코드란?
- 불필요한 어떤 것도 다른 객체에게 보여주지 않으며, 다른 객체의 구현에 의존하지 않는 코드를 말합니다.
묻지 말고 시켜라(Tell, Don't Ask)
- 디미터 법칙은 훌륭한 메시지는 객체의 상태에 관해 묻지 말고 원하는 것을 시켜야 한다는 사실을 강조합니다.
- 묻지 말고 시켜라는 이런 스타일의 메시지 작성을 장려하는 원칙을 가리키는 용어입니다.
- 객체의 외부에서 해당 객체의 상태를 기반으로 결정을 내리는 것은 캡슐화를 위반합니다.
- 묻지 말고 시켜라 원칙을 따르면 객체의 정보를 이용하는 행동을 객체의 외부가 아닌 내부에 위치시키기 때문에 자연스럽게 정보와 행동을 동일한 클래스 안에 두게 됩니다.
- 내부의 상태를 묻는 오퍼레이션을 인터페이스에 포함시키고 있다면 더 나은 방법은 없는지 아래 질문을 던지며 고민해 봐야 합니다.
- 내부의 상태를 이용해 어떤 결정을 내리는 로직이 객체 외부에 존재하는가?
- 만약 중요 로직이 객체 외부에 존재하다면 해당 객체가 책임져야 하는 어떤 행동이 객체 외부로 누수된 것입니다.
- 상태를 묻는 오퍼레이션을 행동을 요청하는 오퍼레이션으로 대체함으로써 인터페이스를 향상해야 합니다.
- 훌륭한 인터페이스를 만들기 위해서는 객체가 어떻게 하는지가 아니라 무엇을 하는지를 서술해야 합니다.
의도를 드러내는 인터페이스
- 구현과 관련된 모든 정보를 캡슐화하고 객체의 퍼블릭 인터페이스에는 협력과 관련된 의도만을 표현한 것을 의미합니다.
어떻게 의도를 드러내는 인터페이스를 만들어야 할까?
- 어떻게가 아니라 무엇을 하는지 드러내는 것입니다.
- 협력을 설계하기 시작하는 이른 시기부터 클래스 내부 구현에 관해 고민할 수밖에 없습니다.
- 무엇을 하는지 드러내도록 메서드의 이름을 짓기 위해서는 객체가 협력 안에서 수행해야 하는 책임에 관해 고민해야 합니다.
- 이것은 외부의 객체가 메시지를 전송하는 목적을 먼저 생각하도록 만들며, 결과적으로 협력하는 클라이언트의 의도에 부합하도록 메서드의 이름을 짓게 됩니다.
- 어떻게 하느냐가 아니라 무엇을 하느냐에 따라 메서드의 이름을 짓는 패턴을 의도를 드러내는 선택자라고 부릅니다.
- 객체에게 묻지 말고 시키되 구현 방법이 아닌 클라이언트의 의도를 드러내야 합니다.
- 이것이 이해하기 쉽고 유연한 동시에 협력적인 객체를 만드는 가장 기본적인 요구사항입니다.
유연한 인터페이스를 만들 때 주의해야 할 점은 무엇일까?
- 잊지 말아야 하는 사실은 설계가 트레이드오프의 산물이라는 것입니다.
- 원칙이 현재 상황에 부적합하다고 판단되면 과감하게 원칙을 무시해야 합니다.
- 원칙을 하는 것보다 더 중요한 것은 언제 원칙이 유용한지 판단할 수 있는 능력을 기르는 것입니다.
- 가끔씩은 필요에 따라 물어야 합니다.
- 소프트웨어 설계에 존재하는 몇 안 되는 법칙 중 하나는 "경우에 따라 다르다"라는 사실을 명심해야 합니다.
명령-쿼리 분리 원칙
- 이 원칙은 퍼블릭 인터페이스에 오퍼레이션을 정의할 때 참고할 수 있는 지침을 제공합니다.
- 어떤 절차를 묶어 호출 가능하도록 이름을 부여한 기능 모듈을 루틴(routine)이라고 부릅니다.
- 루틴은 다시 프로시저(procedure)와 함수(function)로 구분할 수 있습니다.
- 명령과 쿼리는 객체의 인터페이스 측면에서 프로시저와 함수를 부르는 또 다른 이름입니다.
- 객체의 상태를 수정하는 오퍼레이션을 명령이라고 부르고 객체와 관련된 정보를 반환하는 오퍼레이션을 쿼리라고 부릅니다.
- 개념적으로는 명령은 프로시저, 쿼리는 함수와 동일합니다.
- 명령-쿼리 분리 원칙의 요지는 오퍼레이션은 부수효과를 발생시키는 명령이거나 부수효과를 발생시키지 않는 쿼리 중 하나여야 한다는 것입니다.
- 따라서 명령과 쿼리를 분리하기 위해서는 다음 두 가지 규칙을 준수해야 합니다.
- 객체의 상태를 변경하는 명령은 반환값을 가질 수 없습니다.
- 객체의 정보를 반환하는 쿼리는 상태를 변경할 수 없습니다.
- 이 원칙에 따라 작성된 객체의 인터페이스를 명령-쿼리 인터페이스라고 부릅니다.
명령과 쿼리를 분리해서 얻게 되는 장점은 무엇일까?
- 명령이 개입하지 않는 한 쿼리의 값은 변경되지 않기 때문에 쿼리의 결과를 예측하기 쉬우며, 이는 디버깅이 용이한 동시에 유지보수성이 좋아집니다.
- 명령과 쿼리를 분리함으로써 명령형 언어의 틀 안에서 참조 투명성(referential transparency)의 장점을 제한적이나마 누릴 수 있게 됩니다.
참조 투명성(Referential transparency)이란?
- 동일한 입력에 대해 항상 동일한 값을 반환하는 특성을 의미합니다.
참조 투명성을 만족하는 식의 장점은 무엇일까?
- 모든 함수를 이미 알고 있는 하나의 결괏값으로 대체할 수 있기 때문에 식을 쉽게 계산할 수 있습니다.
- 모든 곳에서 함수의 결괏값이 동일하기 때문에 식의 순서를 변경하더라도 각 식의 결과는 달라지지 않습니다.
마무리
오늘은 오브젝트 책 6장을 스터디하면서 중요하다고 생각한 부분을 정리해 보았습니다.
이번 장에서는 어떤 기준으로 인터페이스를 설계하면 좋을지 이해하게 되었고 또 여러 설계 기법들에 대해 알게 되어서 좋았다고 생각이 듭니다.
하지만 설계 기법들을 사용할 때 어떤 부분들을 고려해야할지에 대한 내용이 부족한 거 같아서 조금 아쉽다고 생각이 드네요.
이번 포스팅은 마무리하면서 다음 포스팅에서 뵙겠습니다.
참고
http://www.yes24.com/Product/Goods/74219491
'스터디 > 오브젝트' 카테고리의 다른 글
8장 - 의존성 관리하기 (0) | 2023.05.22 |
---|---|
7장 - 객체 분해 (0) | 2023.04.30 |
5장 - 책임 할당하기 (0) | 2023.04.10 |
4장 - 설계 품질과 트레이드오프 (0) | 2023.03.28 |
3장 - 협력, 책임, 역할 (2) | 2023.03.12 |