오브젝트 - 06. 메시지와 인터페이스

강준혁·2022년 9월 14일
0

오브젝트

목록 보기
7/14
post-thumbnail

객체가 수신하는 메시지들이 객체의 퍼블릭 인터페이스를 구성한다.
훌륭한 퍼블릭 인터페이스를 얻기 위해서는 유연하고 재사용 가능한 퍼블릭 인터페이스를 만드는데 도움이 되는 설계 원칙과 기법을 익히고 적용해야 한다.

협력과 메시지

메시지와 메시지 전송

메시지는 오퍼레이션명과 인자로 구성되며 메시지 전송은 여기에 메시지 수신자를 추가한 것이다. 따라서 메시지 전송은 메시지 수신자, 오퍼레이션명, 인자의 조합이다.

메시지와 메서드

메시지를 수신했을 때 실제로 실행되는 함수 또는 프로시저를 메서드라고 부른다.

중요한 것은 코드 상에서 동일한 이름의 변수에게 동일한 메시지를 전송하더라도 객체의 타입에 따라 실행되는 메서드가 달라질 수 있다는 점이다.
객체는 메시지와 메서드라는 두 가지 서로 다른 개념을 실행 시점에 연결해야 하기 때문에 컴파일 시점과 실행 시점의 의미가 달라질 수 있다.

메시지와 메서드의 구분은 메시지 전송자와 수신자가 느슨하게 결합될 수 있게 한다.

전송자는 수신자가 어떤 클래스의 인스턴스인지, 어떤 방식으로 요청을 처리하는지 모르더라도 원활한 협력이 가능하다.

수신자 역시 메시지를 처리하기 위해 필요한 메서드를 스스로 결정할 수 있는 자율권을 누린다.

퍼블릭 인터페이스와 오퍼레이션

외부의 객체는 오직 객체가 공개하는 메시지를 통해서만 객체와 상호작용할 수 있다. 이처럼 객체가 의사소통을 위해 외부에 공개하는 메시지의 집합을 퍼블릭 인터페이스라고 한다.

프로그래밍 언어의 관점에서 퍼블릭 인터페이스에 포함된 메시지를 오퍼레이션이라고 부른다. 오퍼레이션은 수행 가능한 어떤 행동에 대한 추상화이다.

UML은 공식적으로 오퍼레이션을 다음과 같이 정의한다.
"오퍼레이션이란 실행하기 위해 객체가 호출될 수 있는 변환이나 정의에 대한 명세이다"

인터페이스와 설계 품질

좋은 인터페이스는 최소한의, 추상적인 인터페이스라는 조건을 만족해야 한다.
꼭 필요한 오퍼레이션만을 인터페이스에 포함하며 어떻게가 아닌 무엇을 하는지 표현해야 한다.

디미터 법칙

디미터 법칙은 다음과 같이 설명된다.

"객체의 내부구조에 강하게 결합되지 않도록 협력 경로를 제한하라"
"낯선 자에게 말하지 말라"
"오직 인접한 이웃하고만 말하라"
"오직 하나의 도트만 사용하라"

클래스 내부의 메서드가 아래 조건을 만족하는 인스턴스에만 메시지를 전송하도록 프로그래밍 해야한다 라고 설명할 수도 있다.

  • this 객체
  • 메서드의 매개변수
  • this 의 속성
  • this의 속성인 컬렉션의 요소
  • 메서드 내에서 생성된 지역 객체

디미터법칙을 따르도록 코드를 개선하면 메시지 전송자는 더 이상 메시지 수신자의 내부 구조에 관해 묻지 않게 된다. 단지 자신이 무엇을 원하는지 명시하고 수행하도록 요청한다.

묻지말고 시켜라

"절차적인 코드는 정보를 얻은 후에 결정한다. 객체지향 코드는 객체에게 그것을 하도록 시킨다."

묻지말고 시켜라 원칙을 따르면 객체의 정보를 이용하는 행동을 객체의 외부가 아닌 내부에 위치시키기 때문에 자연스럽게 정보와 행동을 동일한 클래스 안에 두게 된다.

내부의 상태를 묻는 오퍼레이션을 인터페이스에 포함시키고 있다면 더 나은 방법은 없는지 고민하라.

내부의 상태를 이용해 어떤 결정을 내리는 로직이 객체 외부에 존재한다면 해당 객체가 책임져야 하는 어떤 행동이 객체 외부로 누수된 것이다.

의도를 드러내는 인터페이스

메서드의 이름은 두가지 방법으로 명명될 수 있다.
1. 작업을 어떻게 수행하는지 드러내는 메서드명
2. 무엇을 하는지 드러내는 메서드명

어떻게 수행하는지를 드러내는 이름은 메서드의 내부 구현을 설명하는 이름이다.
이는 협력을 설계하기 시작하는 이른 시기부터 클래스의 내부 구현에 관해 고민할 수밖에 없다.

무엇을 하는지 드러내는 메서드명은 객체가 협력 안에서 수행해야 하는 책임에 관해 고민하게 한다. 이는 외부의 객체가 메시지를 전송하는 목적을 먼저 생각하도록 하며 결과적으로 협력하는 클라이언트의 의도에 부합하도록 메서드의 이름을 짓게 된다.

"매우 다른 두 번째 구현을 상상하라. 그리고 해당 메서드에 동일한 이름을 붙인다고 상상해 보라. 그렇게 하면 아마도 그 순간에 당신이 할 수 있는 가장 추상적인 이름을 메서드에 붙일 것이다."

원칙의 함정

원칙이 현재 상황에 부적합하다고 판단되면 과감하게 원칙을 무시하라.
원칙을 아는 것보다 더 중요한 것은 언제 원칙이 유용하고 그렇지 않은지를 판단할 수 있는 능력을 기르는 것이다.

디미터 법칙은 하나의 도트(.)를 강제하는 규칙이 아니다

하나 이상의 도트를 사용하는 모든 케이스가 디미터 법칙 위반인 것은 아니다.
기차충돌처럼 보이는 코드일지라도 객체의 내부 구현에 대한 어떤 정보로 외부로 노출하지 않는다면 그것은 디미터 법칙을 준수한 것이다.

결합도와 응집도의 충돌

묻지 말고 시켜라와 디미터 법칙을 준수하는 것이 항상 긍정적인 결과를 가져오지는 않는다.
모든 상황에서 맹목적으로 위임 메서드를 추가하면 같은 퍼블릭 인터페이스 안에 어울리지 않는 오퍼레이션들이 공존하게 된다.
결과적으로 객체는 상관 없는 책임들을 한꺼번에 떠안게 되기 때문에 결과적으로 응집도가 낮아진다.

디미터 법칙의 위반 여부는 묻는 대상이 객체인지, 자료구조인지에 달려있다.
객체는 내부 구조를 숨겨야 하므로 디미터 법칙을 따르는 것이 좋지만, 자료구조라면 내부를 노출해야 하므로 디미터법칙을 적용할 필요가 없다.

객체에게 시키는 것이 항상 가능한 것은 아니다. 가끔씩은 물어야 한다.

원칙이 적절한 상황을 판단할 수 있는 안목을 길러라.
설계는 트레이드오프의 산물이다.
소프트웨어 설계에 존재하는 몇 안되는 법칙 중 하나는 "경우에 따라 다르다" 라는 사실을 명심하라.

명령 - 쿼리 분리 원칙

명령 - 쿼리 분리 원칙의 요지는 오퍼레이션은 부수효과를 발생시키는 명령이거나 부수효과를 발생시키지 않는 쿼리 중 하나여야 한다는 것이다.

이를 위해서는 다음의 두 가지 규칙을 준수해야 한다.

  • 객체의 상태를 변경하는 명령은 반환값을 가질 수 없다.
  • 객체의 정보를 반환하는 쿼리는 상태를 변경할 수 없다.

명령과 쿼리를 엄격하게 분류하면 객체의 부수효과를 제어하기가 수월해진다.

쿼리는 객체의 상태를 변경하지 않기에 몇번이고 반복적으로 호출되어도 상관이 없다.
명령이 개입되지 않는 한 쿼리의 값은 변경되지 않기에 결과 예측이 쉬워진다.
또한 쿼리의 순서를 자유롭게 변경이 가능하다.

부수효과를 얘기할 때 빠질 수 없는 것이 참조 투명성 이다.
참조 투명성이란 "어떤 표현식 e가 있을 때 e의 값으로 e가 나타내는 모든 위치를 교체하더라도 결과가 달라지지 않는 특성"을 의미한다.

참조 투명성을 만족하는 식은 우리에게 두 가지 장점을 제공한다

  • 모든 함수를 이미 알고있는 결과값으로 대체 가능하기에 식의 계산이 쉽다.
  • 모든 곳에서 함수의 결과값이 동일하기에 식의 순서를 변경해도 결과가 동일하다.

객체지향 패러다임은 객체의 상태변경이라는 부수효과를 기반으로 하기에 참조 투명성은 예외에 가깝다.
하지만 명령 - 쿼리 분리 원칙을 적용하면 부수효과를 가지는 명령으로부터 부수효과를 가지지 않는 쿼리를 명백하게 분리함으로서 제한적이나마 참조 투명성의 혜택을 누릴 수 있다.

profile
백엔드 개발자

0개의 댓글