목차
개요
: 객체가 행동해야 한다는 것은 무슨 의미이며, 어떻게 해야 객체를 행동하게 만들 수 있을까 ?
- 답은 TDA 원칙을 적용하는 것이다.
: TDA 원칙은 객체를 행동하게 만든다
- 이유는 “묻지 말고 시켜라” 라는 말 자체가 객체에 어떤 행동을 하라고 조언하는 것이기 때문이다.
- 하지만 TDA 원칙은 객체가 이미 존재한다는 것을 전제로 사용할 수 있는 원칙이다.
- 이제 막 객체를 설계하는 단계라면 어떻게 행동에 집중하는 객체를 만들 수 있을까?
: 행동 위주의 사고를 하는 편이 객체지향에서는 훨씬 유리하다고 볼 수 있다.
- 객체를 구분 짓는 요인은 데이터가 아닌 행동이다.
- 객체를 만들 때는 데이터보다는 행동에 집중해야한다. 데이터가 객체를 결정하지 않는다.
- 데이터로는 객체를 정의하기 어렵지만 행동을 보면 객체를 정의하기 쉽다.
3.1 덕 타이핑
: 덕 타이핑
- 덕 타이핑의 개념은 덕 테스트에서 유래했다.
- 덕 테스트
- "만약 어떤 새가 오리처럼 걷고 헤엄치고 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다."
- 개발자 관점에서 이 말을 해석하자면 “행동이 같다면 같은 클래스로 부르겠다”라는 의미이다.
- 덕 테스트
- 자바 개발자의 상식에서 벗어나 “행동이 같으면 같은 클래스로 보겠다” 라는 덕 타이핑의 개념이 다른 언어에서는 어떻게 구현됐는지를 보고 확인하면 좋을 것
- 타입스크립트에서는 행동이 같은 두 클래스를 같은 클래스로 보겠다는 덕 타이핑의 개념을 지원한다.
3.2 행동과 구현
: 메서드를 구현하려고 했더니 데이터 위주의 사고로 돌아온다.
- 행동의 구현을 고민했더니 결국 이 클래스가 어떤 값을 갖고 있어야하는지를 고민하게 됐다 이는 그다지 반가운 현상이 아니다.
- 행동을 고민하면서 구현이나 알고리즘을 고민해서는 안된다.
- 행동을 고민하는 순간에는 순수하게 이 클래스에 어떤 동작을 시킬 수 있을 것인지만 고민하는 것이 좋다오롯이 어떤 메시지를 처리할지만 고민하는 것이다. “어떻게”는 그 다음 문제다.
: 어떻게 해야 구현에 구애받지 않고 행동만 고민할 수 있을까?
자바의 인터페이스를 활용하면 구현 없이도 메서드를 정의할 수 있다. 인터페이스에는 오롯이 어떤 행동을 어떻게 시킬 수 있는지만 선언할 수 있는 것이다. 덕분에 개발자는 메시지를 전달하는 방법만 신경쓸 수 있다.
: 구현은 신경 쓰지 않아도 괜찮나?
- 초기 설계 단계에서는 상세한 구현은 무시해도 괜찮다.(p83)
- 내부의 상세 구현은 인터페이스의 구현체를 개발하는 분이 협의한 요구사항에 맞춰 알아서 개발할것이다.
: “남이 알아서 개발해줄 테니 상관없다”라는 자세가 무책임하진 않나?
의외로 그렇지 않다. 누군가 알아서 잘 개발해 줄 것이라고 믿는 것이며 이러한 믿음의 자세는 오히려 바람직하다. 이와 같은 방식이 협업을 용이하게 만든다.
물론 이러한 작업 방식은 협력 객체를 개발하는 팀원이 인터페이스를 제대로 지켜오지 않을 때 문제가 발생한다.
- 이 경우에는 어떻게 처리하나 ?
- 답은 “그래서는 안된다”이다. 왜냐하면 우리는 이미 인터페이스를 기반으로 협력 방식을 정해 놓았기 떄문이다. 이럴 때는 이해관계자들이 모여 설계를 다시해야한다.
- 인터페이스는 반드시 지켜져야 한다. 그래서 “인터페이스는 곧 계약이다”와 같은 말이 있음
3.3 인터페이스
: 인터페이스와 행동은 다르다
행동이 곧 인터페이스는 아니다.
인터페이스는 외부에서 어떤 객체에게 행동을 시키고자 할 때 메시지를 보낼 수 있는 창구일 뿐이다. “어떤 행동을 지시하는 방법의 집합” 정도로 표현할 수 있다.
API와 마찬가지로 자바의 인터페이스는 어떤 객체를 어떻게 사용하면되는지 외부 객체에게 알려주는 것과 같다.
이러한 맥락에서 봤을 때 쉽게 이해되는 사실 두가지가 있다.
- 자바 인터페이스에는 private 선언이 불가능하다.
- 인터페이스는 외부 세계에 “나”를 다루는 방법을 알려주는 것이기에 “나만 아는 사용법”이 존재한다는 것은 부자연 스럽다. 따라서 인터페이스에 private 메서드가 존재한다는 것 자체가 이상하다.
- 인터페이스의 메서드에는 public을 지정하면 IDE에서 public을 선언할 필요가 없다고 안내한다.
- 인터페이스의 목적이 “외부 세계”에 나를 다루는 방법을 알려주는 것이므로 별도의 지시자가 없이도 public으로 동작한다.
3.4 행동과 역할
: 우리는 왜 데이터 위주 사고를 할 수 밖에 없나?
- 우리는 요청을 받았을 때 “용어” 때문에 데이터 위주의 사고를 하게 된다.
- 예를 들어 “자동차”가 역할보다는 구현에 가까운 용어이기 때문이다.
- 자동차가 어떤 행동을 할 수 있을지 모른다. 그러니 “자동차를 만들어달라”라는 요청을 받고 “필요한 부품, 구현하는데 필요한 속성은 뭐가 있을까” 정도 밖에 떠올릴 수 없다.
- 예를 들어 “자동차”가 역할보다는 구현에 가까운 용어이기 때문이다.
: 우리는 실체에 집중할 때 데이터 위주의 사고를 하고, 역할에 집중할 때 행동 위주의 사고를 한다. (p87)
- 실체는 곧 구현이기 때문이다. 메서드의 구현을 고민했더니 데이터 위주의 사고를 했던 것처럼 구현을 고민하는 것은 데이터 위주의 사고를 촉진한다.
- 역할을 고민하지 않고 구현체에 집중하면 해당 클래스를 개발하는 개발자만의 생각이 반영된 클래스가 나오게된다.
- 따라서 “자동차 클래스를 만들어 줄 수 있나요?” 같은 요청을 받았을 때 다음과 같은 질문을 역으로 했다면 좋을 것
- 자동차는 어떤 행동을 하는 객체인가요 ?
- 꼭 자동차여야 하나요?
- 자동차라는 클래스를 만들어서 달성하려는 목표가 뭐지요 ?
- 따라서 “자동차 클래스를 만들어 줄 수 있나요?” 같은 요청을 받았을 때 다음과 같은 질문을 역으로 했다면 좋을 것
: 구현에 집중한 코드는 확장되는 요구사항에 유연하게 대처할 수 없다.
- 역할에 집중해야 유연한 설계를 얻을 수 있다. 하지만 아쉽게도 우리는 구현체를 먼저 상상하는데 익숙하다.
- 역할에 집중하는 사고 방식을 익히기 위해서는 꾸준한 훈련이 필요하다. 반복적이고 의식적으로 객체가 어떤 행동을 해야 하는지 고민해야 한다.
- 더 나아가 어떤 행동을 모아 구조적으로 타당한 역할을 만들 수 있을지 계속해서 고민해야 한다.
3.5 메서드
: 함수와 메서드는 무엇이 다를까?
- 함수는 다음의 정의를 갖는다.
- “함수는 입력값과 출력값 사이의 대응 관계를 나타낸다. 함수의 각 입력은 정확히 하나의 출력값으로 대응된다”
- 함수는 같은 입력에 대해 항상 같은 출력을 해야한다. 같은 입력값으로 함수를 함수를 실행했을 때 다른 출력값이 나와서는 안된다.
- 즉, 같은 입력에 대해 두 개의 출력을 갖는 함수가 있어서는 안된다.
- 함수는 같은 입력에 대해 항상 같은 출력을 해야한다. 같은 입력값으로 함수를 함수를 실행했을 때 다른 출력값이 나와서는 안된다.
- “함수는 입력값과 출력값 사이의 대응 관계를 나타낸다. 함수의 각 입력은 정확히 하나의 출력값으로 대응된다”
- 객체지향에서는 특정한 구현에 의존하는 상황을 피하고자 한다. 그래서 객체지향에서는 협력 객체에 어떤 일을 요청할 때 “함수를 실행한다”라는 말 대신 “메시지를 전달한다”라고 표현한다.
- 객체지향에서 객체들은 메시지를 통해 소통한다.
- 즉, 객체는 협력 객체에 메시지만 보낼 뿐이다. 실제로 어떤 방법(method)으로 일을 어떻게 처리할지는 객체가 정한다.
- 그래서 객체지향에서는 객체가 수행하는 함수를 메서드라 부른다.
- 메서드란 어떤 메시지를 처리해 달라는 요청을 받았을 때 이를 어떻게 처리하는지 방법을 서술하는 것이다.
- 따라서 메서드를 어떻게 구현할지에 집중하는 것은 그다지 좋은 방법이 아니다. 메서드란 결국 어떤 메시지를 어떻게 처리하는지 서술하는 것이므로 알고리즘에 가까운 것이기 때문이다.
- 객체지향에서 객체들은 메시지를 통해 소통한다.
객체지향에서 진짜 중요한 것은 책임을 나눈고 메시지를 통해 협력관계를 구축하는 것이다.