////
Search
Duplicate
🔮

Chapter 03. 행동

저자는 객체지향 프로그래미에서 객체는 단순한 데이터 덩어리가 아니며 마치 자아를 가진 것처럼 동작해야 한다고 서술한 바 있다.
이 말은 곧 객체가 데이터로서 존재하는 것이 아니라 ‘행동’을 수행할 수 있음이 중요하다는 것을 의미한다.
그렇다면 어떻게 해야 객체가 행동하게 만들 수 있을까? 사실 우리는 이미 가장 쉽게 객체가 행동하게 만드는 방법을 알고 있다. 바로 TDA 원칙을 적용하는 것이다.
TDA 원칙은 객체를 행동하게 만든다. TDA 원칙이 객체를 행동하게 한다고 하는 이유는 ‘묻지 말고 시켜라’라는 말 자체가 객체로 하여금 어떤 행동을 하게 만들도록 조언하는 것이기 때문이다.
TDA 원칙은 속성 혹은 상태를 이미 가지고 있는 객체가 있을 때 사용하기 좋은 원칙이다. TDA 원칙은 수동적인 객체를 능동적인 객체로 바꾸는 방법이므로 객체가 이미 존재함을 전제로 사용할 수 있다.
그렇다면 아직 정의된 속성도 없고 이제 막 객체를 설계하는 단계라면 어떻게 행동에 집중하는 객체를 만들 수 있을까?
이번 장 시작에 앞서 질문
자동차 클래스를 만들어 줄 수 있나요?
이 질문에 자동차가 가지고 있는 속성, 타이어나 핸들 등을 떠올린다면 초기 설계는 구조체에 불과한 객체가 될 것이다.
이 질문에 자동차가 수행해야하는 책임인 행동, 가속, 감속, 운행 등을 떠올린다면 객체지향적인 프로그래밍이 쉬워질 것이다.
객체를 구분 짓는 요인은 데이터가 아니다. 행동이다. 우리는 객체를 만들 때 데이터보다는 행동에 집중해야 합니다. 데이터 객체를 결정하진 않는다.
데이터로는 객체를 정의하기 어렵다. 추상적이기 때문이다. 그러나 행동을 보면 객체를 정의하기 쉬워진다.
행동과 역할 행동을 먼저 고민했더니 자동차나 자전거가 아닌 추상적인 탈 것이라는 역할이 먼저 떠오르게 된다. 행동을 고민하는 것은 자연스럽게 역할을 고민하게 만든다. 따라서 어떤 행동을 수행할 수 있는지가 곧 역할을 만든다. 그리고 이러한 여러 역할들이 모여 객체를 정의하게 된다.

1. 덕 타이핑

객체지향은 행동을 강조한다. 그리고 이 사실을 뒷받침하는 데 사용하기 좋은 용어가 하나 있는데 바로 덕 타이핑이다.
덕 타이핑은 덕 테스트에서 유래했다. 덕 테스트: 만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.
개발자 관점에서 이 말을 해석하면 ‘행동이 같다면 같은 클래스로 부르겠다’라는 의미다.
이펙티브 타입스크립트에서 봤던 내용과 연결되는 내용이었다. 역시 타입스크립트는 충실히 객체지향의 개념을 포함하고 있구나라는 생각이 들었다.
타입스크립트에서 타입 체커는 타입 체크 수행 시, 선언된 매개변수와 전달된 매개변수의 속성이 일치한다면 같은 타입으로 보고 문제 없이 통과시킨다.
행동이 곧 역할을 정의하고, 역할이 곧 객체를 정의한다.

2. 행동과 구현

그렇다면 행동을 정의하는 것만으로도 충분할까? 아니다. 구현에 대한 사고도 해야 한다.
구현에 대한 사고를 수행하면 객체는 어떻게 변화할까? 상태가 생기게 된다. 메시지를 받아 처리하기 위해 상태를 변경시키거나 어떠한 값을 반환시키기 때문이다.
구현에 구애받지 않고 행동만 고민할 수 있는 방법은 뭘까? 바로 인터페이스를 사용하면 된다. 자바의 인터페이스를 사용하면 구현 없이도 메소드를 정의할 수 있다.
물론 디폴트 메소드와 같이 일부 구현을 제공할 수는 있지만 이는 적절한 방법이 아니다. 인터페이스 설계 단계에서는 인터페이스만 고민해야 한다.

3. 인터페이스

인터페이스와 행동은 다르다, 행동에 집중하다보니 인터페이스에 관한 설명으로 이어졌지만 행동이 곧 인터페이스는 아니다.
인터페이스는 외부에서 어떤 객체에게 행동을 시키고자 할 때 메시지를 보낼 수 있는 창구에 불과하다.
조금 더 딱딱한 표현으로는 어떤 행동을 지시하는 방법의 집합 정도로 표현할 수 있다.
인터페이스는 나를 사용하려면 이렇게 사용하면 돼라고 외부에 알려주는 명세와 같다. 여기서 는 객체가 될 수도 있고 시스템이 될 수도 있다.
객체들이 인터페이스를 통해 통신하게 되면 객체 간의 결합도를 낮출 수 있다. 그 결과 유연성과 확장성을 얻을 수 있다.
인터페이스를 사용하는 코드는 재사용성도 높아지고 유지보수성도 올라가며 모듈화도 가능해진다.
하지만 가장 좋은 점은 행동과 역할을 고민할 수 있게 해준다는 것이다. 그러므로 인터페이스는 중요하다.

4. 행동과 역할

사실 앞선 자동차 클래스를 만들어 줄 수 있냐는 질문은 사실 데이터 위주의 사고를 하도록 유도한 요청이었다.
왜 우리는 데이터 위주의 사고를 하게 되었을까? 자동차라는 용어가 포함되었기 때문이다. 자동차는 역할보다는 구현에 가까운 용어이기 때문이다.
우리는 역할 위주의 사고를 해야 한다. 자동차는 실체이고 탈 것은 역할이기 때문이다.
구현에 집중한 코드는 확장되는 요구사항들에 유연하게 대처하기가 힘들다. 역할에 집중해야 유연한 설계를 얻을 수 있다.
따라서 역할에 집중하는 사고 방식을 익히기 위해서는 꾸준한 훈련이 필요하다.
반복적이고 의식적으로 객체가 어떤 행동을 수행해야 하는 지 고민하자. 더 나아가 어떤 행동들을 모아 구조적으로 타당한 역할을 만들 수 있을지 계속해서 고민하자.

5. 메소드

메소드는 왜 메소드일까? 방법을 뜻하는 메소드와 함수에는 어떤 차이가 있는 걸까?
함수란 함수의 각 입력값이 정확히 하나의 출력값으로 대응된다는 정의를 가진다. 즉 함수는 같은 입력에 대해 항상 같은 출력을 해야 한다.
그렇다면 메소드는? 메소드는 다형성이 적용된 이상 디스패처가 결정하기 전까진 어떤 것이 수행될지 모른다. 물론 맥락을 읽어서 알 순 있겠지만..
객체지향에서 객체들은 메시지를 통해 소통한다. 이러이러한 인수를 건네줄 테니 뭔가를 처리해달라고 부탁하는 것이다. 즉 객체는 협력 객체에 메시지만 보낼 뿐이다.
실제로 어떤 방법(method)로 일을 어떻게 처리할지는 객체가 결정하며 객체만이 알고 있다.
따라서 객체지향에서는 객체가 수행하는 함수를 메소드라고 부른다. 메소드란 어떤 메시지를 처리해달라는 요청을 받았을 때 이를 어떻게 처리하는 지 방법(method)를 서술하는 것이다.
이때문에 메소드를 어떻게 구현할지에 집중하는 것은 그다지 좋은 방법이 아니다. 왜냐면 결국 메소드란 결국 어떤 메시지를 어떻게 처리할지 서술하는 것이므로 알고리즘에 가까운 것이기 때문이다.
알고리즘을 고민하면 결국 구현에 집착하게 된다. 그러면 객체의 메소드를 작성하는 행위가 함수를 작성하는 것과 다를바 없어지고 절차지향적인 코드가 나오게 되는 것이다.