이번 장의 주제는 객체와 자료구조이다. 주제만으로 내용을 연상하기 매우 힘들다.
무엇을 설명하려고 하는 것일까?
시작은 다음과 같은 글로 시작한다.
변수를 private 하게 정의하는 데는 이유가 있다. 남들이 변수에 의존하지 않게 만들고 싶어서이다.
이를 통해 변수의 타입이나 구현을 맘대로 변경하고 싶어서이다.
그렇다면 어째서 수많은 프로그래머가 get/set 함수를 당연하게 public 하게 공개해 비공개 변수를 외부에 노출할까?
여기서는 당신은 객체를 자율적인 객체로 설계하고 있는가에 대한 질문을 던진다.
우리는 아무런 생각 없이 클래스를 설계할 때 private 키워드를 통해 객체의 상태를 감춘다.
여기서 우리는 왜 감춰야 하냐고 묻는다면 어떻게 대답할 것인지 궁금하다.
우리가 만약 public 하게 내부 구현을 외부로 노출하도록 클래스를 설계했다고 하자.
이것이 자료 구조체가 아닌 객체로써 자유로울까?
그렇지 않다.
이 객체는 자신의 행위가 타 객체에서 해당 상태를 간섭하여 메시지를 처리하는 방법에 있어서 제한을 받게 될 것이다. 또한 상태를 설정할 수 있는 방법을 강제하게 된다.
이를 해결하기 위해 상태를 감추고 get함수와 set함수를 제공하면 해결이 될 거라 생각하면 안 된다.
변수 사이에 함수라는 계층을 넣는다고 구현이 자동적으로 감춰지지는 않는다. 결국 구현을 감추기 위해서는 추상화는 필수불가결이라 생각된다.
상태를 다루려는 시도보다는 추상화 인터페이스를 통해 객체의 행동을 노출해야 하고 사용자는 행동을 통해 객체의 상태를 조작할 수 있어야 한다.
사실 이는 객체지향 설계적인 부분에서 객체의 상태에 의존하기보다 협력안에서 객체가 어떠한 행동을 해야 하는지 생각하여 메시지를 할당하게 되면 자연스럽게 처리되는 부분으로 보인다.
추상화를 통해 자연스럽게 객체의 내부가 감춰질 것이다.
자료를 세세하게 공개하기보다는 추상적인 개념으로 표현하는 편을 권한다.
단순히 인터페이스와 set/get 함수만으로 추상화가 이루어지지 않는다는 것도 알아야 한다.
개발자는 객체가 포함하는 자료를 표현할 가장 좋은 방법을 고민해야 한다.
아무 생각 없이 get/set 함수를 추가하는 것은 추상화에 어떠한 도움도 되지 않는다.
객체와 자료는 비 대칭하다.
객체지향과 절차 지향에 대해서 말하고 있다.
이 둘은 상호보완적인 특질이 존재하기에 서로 상반된 개념이다.
그렇다면 어떠한 부분이 상호보완적일까?
절차적인 코드는 기존 자료구조를 변경하지 않으면서 새로운 함수를 추가하기 매우 쉽다.
새로운 함수가 기존 자료구조에 영향을 미치지 않고 사용하는 쪽에서 기존 자료구조를 사용해 함수를 쉽게 추가할 수 있기 때문이다. 반면 객체 지향 코드는 기존 함수를 변경하지 않고 새 클래스를 추가하기 쉽다.
역으로 생각하면 절차적인 코드는 새로운 자료구조를 추가하기 매우 어렵다. 사용자 쪽에서 모든 함수를 고쳐야 하기 때문이다.
또한 객체지향 코드는 새로운 함수를 추가하기 어려운데 인터페이스의 행동의 추가를 한다면 해당 인터페이스에 영향을 받는 모든 구현체들이 영향을 받기 때문이다.
복잡한 시스템을 만들다 보면 새로운 함수가 아닌 새로운 자료 타입이 필요한 경우가 있는데 이 경우에는 클래스와 객체지향 기법이 적합하다. 반면 새로운 자료 타입이 아닌 새로운 함수가 필요한 경우도 생기는데, 이 경우에는 절차적인 코드와 자료 구조가 좀 더 적합하다.
다음은 이 모든 부분을 정리하는 글을 가져왔다.
분별력 있는 프로그래머는 모든 것이 객체라는 생각이 미신임을 잘 안다. 때로는 단순한 자료 구조와 절차적인 코드가 가장 적합한 상황도 있다.
자료 전달 객체
이 부분은 내가 매우 혼동되는 부분으로 최대한 이해하여 짚고 넘어갈 부분이다. 바로 DTO이다.
자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스이다.
이를 Data Transfer Obejct, DTO라 한다. DTO는 굉장히 유용한 구조체이다. 특히 데이터베이스와 통신하거나 소켓에서 받는 메시지의 구문을 분석할 때 유용하다.
흔히 DTO는 가공되지 않은 정보를 애플리케이션 코드에서 사용될 객체로 변환하는 일련의 단계에서 가장 처음으로 사용하는 구조체이다. 좀 더 일반적인 형태는 bean*구조이다.
(* Java Bean을 의미하며 필드는 private로 구성되어 getter와 setter를 통해서만 접근할 수 있고, 전달 인자가 없는 생성자를 가지는 형태의 클래스이다.)
저자는 Bean을 싫어하는 것 같다. 사이비 캡슐화로, 일부 OO 순수 주의자나 만족시킬 뿐 별다른 이익을 제공하지 않는다고 말한다.
DTO의 특수한 형태로 활성 레코드가 존재한다.
DTO와 동일한 구조지만 대개 save나 find와 같은 탐색 함수도 제공한다. 이러한 활성 레코드는 데이터베이스 테이블이나 다른 소스에서 자료를 직접 반환한다.
이러한 활성 레코드에 비즈니스 규칙 메서드를 추가해 이런 자료 구조를 객체로 취급하는 개발자가 흔하다. 하지만 이는 바람직하지 않다.
이게 바로 자료 구조도 아니고 객체도 아닌 잡종 구조를 발생시키기 때문이다.
그래서 보통 외부에서 받은 데이터를 보관하는 DTO와 도메인 객체를 분리시키는 것으로 알 고 있다.
또 다른 예시는 웹 서비스에서 클라이언트가 전송한 데이터를 보관하는 객체와 도메인 객체를 분리시킨다. 당연한 거지만 잘 안 지켜지는 경우도 보긴 했다...
( 내가 생각하는 이유는 서버는 클라이언트를 기본적으로 신뢰하지 않는다는 면에서 보안성을 위협하고 클라이언트에 의존적인 설계로 변질되어 유지보수성이 매우 떨어지는 구조로 변질된다.)
저자도 동일한 해결책을 내놓는다.
비즈니스 규칙을 담으면서 내부 자료를 숨기는 객체는 따로 생성하고 활성 레코드는 자료 구조로 취급해야 한다.
이는 비즈니스 규칙을 적용하지 말고 탐색을 위한 알고리즘 정도를 추가하라는 것으로 받아들이겠다.
결론적으로 저자는 다시 한번 강조한다. 우수한 소프트웨어 개발자는 편견 없이 다양한 개념을 이해하고 직면한 문제에 최적인 해결책을 선택한다.
'독서에서 한걸음' 카테고리의 다른 글
Clean Code .Part9 (0) | 2022.07.30 |
---|---|
Clean Code .Part7 (0) | 2022.04.21 |
Clean Code .Part5 (0) | 2022.04.10 |
Clean Code .Part4 (0) | 2022.04.09 |
Clean Code .Part3 (0) | 2022.04.07 |
댓글