•
저자는 객체지향 프로그래미에서 객체는 단순한 데이터 덩어리가 아니며 마치 자아를 가진 것처럼 동작해야 한다고 서술한 바 있다.
◦
이 말은 곧 객체가 데이터로서 존재하는 것이 아니라 ‘행동’을 수행할 수 있음이 중요하다는 것을 의미한다.
•
그렇다면 어떻게 해야 객체가 행동하게 만들 수 있을까? 사실 우리는 이미 가장 쉽게 객체가 행동하게 만드는 방법을 알고 있다. 바로 TDA 원칙을 적용하는 것이다.
◦
TDA 원칙은 객체를 행동하게 만든다. TDA 원칙이 객체를 행동하게 한다고 하는 이유는 ‘묻지 말고 시켜라’라는 말 자체가 객체로 하여금 어떤 행동을 하게 만들도록 조언하는 것이기 때문이다.
•
TDA 원칙은 속성 혹은 상태를 이미 가지고 있는 객체가 있을 때 사용하기 좋은 원칙이다. TDA 원칙은 수동적인 객체를 능동적인 객체로 바꾸는 방법이므로 객체가 이미 존재함을 전제로 사용할 수 있다.
◦
그렇다면 아직 정의된 속성도 없고 이제 막 객체를 설계하는 단계라면 어떻게 행동에 집중하는 객체를 만들 수 있을까?
•
이번 장 시작에 앞서 질문
자동차 클래스를 만들어 줄 수 있나요?
◦
이 질문에 자동차가 가지고 있는 속성, 타이어나 핸들 등을 떠올린다면 초기 설계는 구조체에 불과한 객체가 될 것이다.
◦
이 질문에 자동차가 수행해야하는 책임인 행동, 가속, 감속, 운행 등을 떠올린다면 객체지향적인 프로그래밍이 쉬워질 것이다.
•
객체를 구분 짓는 요인은 데이터가 아니다. 행동이다. 우리는 객체를 만들 때 데이터보다는 행동에 집중해야 합니다. 데이터 객체를 결정하진 않는다.
◦
데이터로는 객체를 정의하기 어렵다. 추상적이기 때문이다. 그러나 행동을 보면 객체를 정의하기 쉬워진다.
행동과 역할
행동을 먼저 고민했더니 자동차나 자전거가 아닌 추상적인 탈 것이라는 역할이 먼저 떠오르게 된다.
행동을 고민하는 것은 자연스럽게 역할을 고민하게 만든다. 따라서 어떤 행동을 수행할 수 있는지가 곧 역할을 만든다. 그리고 이러한 여러 역할들이 모여 객체를 정의하게 된다.
1. 덕 타이핑
•
객체지향은 행동을 강조한다. 그리고 이 사실을 뒷받침하는 데 사용하기 좋은 용어가 하나 있는데 바로 덕 타이핑이다.
◦
덕 타이핑은 덕 테스트에서 유래했다. 덕 테스트: 만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.
◦
개발자 관점에서 이 말을 해석하면 ‘행동이 같다면 같은 클래스로 부르겠다’라는 의미다.
•
이펙티브 타입스크립트에서 봤던 내용과 연결되는 내용이었다. 역시 타입스크립트는 충실히 객체지향의 개념을 포함하고 있구나라는 생각이 들었다.
◦
타입스크립트에서 타입 체커는 타입 체크 수행 시, 선언된 매개변수와 전달된 매개변수의 속성이 일치한다면 같은 타입으로 보고 문제 없이 통과시킨다.
•
행동이 곧 역할을 정의하고, 역할이 곧 객체를 정의한다.
2. 행동과 구현
•
그렇다면 행동을 정의하는 것만으로도 충분할까? 아니다. 구현에 대한 사고도 해야 한다.
•
구현에 대한 사고를 수행하면 객체는 어떻게 변화할까? 상태가 생기게 된다. 메시지를 받아 처리하기 위해 상태를 변경시키거나 어떠한 값을 반환시키기 때문이다.
•
구현에 구애받지 않고 행동만 고민할 수 있는 방법은 뭘까? 바로 인터페이스를 사용하면 된다. 자바의 인터페이스를 사용하면 구현 없이도 메소드를 정의할 수 있다.
•
물론 디폴트 메소드와 같이 일부 구현을 제공할 수는 있지만 이는 적절한 방법이 아니다. 인터페이스 설계 단계에서는 인터페이스만 고민해야 한다.
3. 인터페이스
•
인터페이스와 행동은 다르다, 행동에 집중하다보니 인터페이스에 관한 설명으로 이어졌지만 행동이 곧 인터페이스는 아니다.
•
인터페이스는 외부에서 어떤 객체에게 행동을 시키고자 할 때 메시지를 보낼 수 있는 창구에 불과하다.
◦
조금 더 딱딱한 표현으로는 어떤 행동을 지시하는 방법의 집합 정도로 표현할 수 있다.
•
인터페이스는 나를 사용하려면 이렇게 사용하면 돼라고 외부에 알려주는 명세와 같다. 여기서 나는 객체가 될 수도 있고 시스템이 될 수도 있다.
•
객체들이 인터페이스를 통해 통신하게 되면 객체 간의 결합도를 낮출 수 있다. 그 결과 유연성과 확장성을 얻을 수 있다.
◦
인터페이스를 사용하는 코드는 재사용성도 높아지고 유지보수성도 올라가며 모듈화도 가능해진다.
◦
하지만 가장 좋은 점은 행동과 역할을 고민할 수 있게 해준다는 것이다. 그러므로 인터페이스는 중요하다.
4. 행동과 역할
•
사실 앞선 자동차 클래스를 만들어 줄 수 있냐는 질문은 사실 데이터 위주의 사고를 하도록 유도한 요청이었다.
•
왜 우리는 데이터 위주의 사고를 하게 되었을까? 자동차라는 용어가 포함되었기 때문이다. 자동차는 역할보다는 구현에 가까운 용어이기 때문이다.
•
우리는 역할 위주의 사고를 해야 한다. 자동차는 실체이고 탈 것은 역할이기 때문이다.
•
구현에 집중한 코드는 확장되는 요구사항들에 유연하게 대처하기가 힘들다. 역할에 집중해야 유연한 설계를 얻을 수 있다.
◦
따라서 역할에 집중하는 사고 방식을 익히기 위해서는 꾸준한 훈련이 필요하다.
◦
반복적이고 의식적으로 객체가 어떤 행동을 수행해야 하는 지 고민하자. 더 나아가 어떤 행동들을 모아 구조적으로 타당한 역할을 만들 수 있을지 계속해서 고민하자.
5. 메소드
•
메소드는 왜 메소드일까? 방법을 뜻하는 메소드와 함수에는 어떤 차이가 있는 걸까?
•
함수란 함수의 각 입력값이 정확히 하나의 출력값으로 대응된다는 정의를 가진다. 즉 함수는 같은 입력에 대해 항상 같은 출력을 해야 한다.
◦
그렇다면 메소드는? 메소드는 다형성이 적용된 이상 디스패처가 결정하기 전까진 어떤 것이 수행될지 모른다. 물론 맥락을 읽어서 알 순 있겠지만..
•
객체지향에서 객체들은 메시지를 통해 소통한다. 이러이러한 인수를 건네줄 테니 뭔가를 처리해달라고 부탁하는 것이다. 즉 객체는 협력 객체에 메시지만 보낼 뿐이다.
•
실제로 어떤 방법(method)로 일을 어떻게 처리할지는 객체가 결정하며 객체만이 알고 있다.
◦
따라서 객체지향에서는 객체가 수행하는 함수를 메소드라고 부른다. 메소드란 어떤 메시지를 처리해달라는 요청을 받았을 때 이를 어떻게 처리하는 지 방법(method)를 서술하는 것이다.
•
이때문에 메소드를 어떻게 구현할지에 집중하는 것은 그다지 좋은 방법이 아니다. 왜냐면 결국 메소드란 결국 어떤 메시지를 어떻게 처리할지 서술하는 것이므로 알고리즘에 가까운 것이기 때문이다.
◦
알고리즘을 고민하면 결국 구현에 집착하게 된다. 그러면 객체의 메소드를 작성하는 행위가 함수를 작성하는 것과 다를바 없어지고 절차지향적인 코드가 나오게 되는 것이다.