TDA 원칙이라고도 불린다.

우리 말로 번역해 보자면 "물어보지 말고 그냥 시켜라"가 될 수 있다.

이는 객체와 객체가 협력하는 경우, 다른 객체에게 정보를 요구하지 말고 그냥 행위하도록 시키라는 의미이다. 즉 정보 은닉의 중요성을 강조하는 원칙이라고 할 수 있겠다.


정보를 처리하는 소프트웨어 구현의 경제성 관점에서 보면 이렇다.

소프트웨어의 복잡성은 다루어야 할 정보의 양에 영향을 받는다. 다루어야 할 정보가 많아지면 더 많은 정보를 가공해야 하고, 정보의 값에 의한 제어 변경(보통 상태라고 부른다)이 더 자주 발생하게 된다. 

하지만 꼭 정보의 양만 복잡도를 가늠하는 척도는 아니다. 정보를 처리하는 단계가 짧고, 정보를 다루어야 할 객체가 적고, 정보에 대한 처리 과정을 중복되지 않고 간결하게 처리한다면 같은 데이터를 처리하더라도 훨씬 단순한 소프트웨어를 만들 수 있을 것이다. 이 과정에서 TDA 원칙의 중요성이 떠오르게 된다. 정보를 입수했을 때 그 정보를 한정적인 범위 내에서만 다루도록 하고(예를 들어 단일 객체), 혹시 외부에서 그 정보에 기반하여 동작을 수행해야 할 경우에는 정보를 가지고 있는 쪽에 동작을 요청하도록 하면 넓은 범위에서 데이터를 입수하여 처리하는 방식에 비해서 훨씬 복잡도가 줄어들게 될 것이다.


아래 그림을 살펴보자.




이 그림에서 데이터는 최초로 객체1에 전달된다. 그림 상에서는 객체1이 받은 데이터를 객체2와 객체3에 주고, 객체3은 이를 다시 객체 4에 준다. 이렇게 데이터가 전달되는 방식은 크게 두 가지가 있다.

1. getter를 통해 데이터를 요청하는 경우.

2. 다른 객체의 API에 데이터를 인자로 넣게 되어 있는 경우.


Tell, don't ask라는 것은 1번에 해당하는 말이다. 즉, 데이터를 getter로 요청하지 말 것을 의미한다. 하지만 데이터를 전파하는 방법은 2도 해당하므로 이 두가지 경우가 발생하지 않도록 설계해야 한다.

자, 그림 상에서 1번이든 2번이든 어떤 방식으로든 데이터를 전달하도록 설계했다고 하자. 그러면 저 그림의 모든 객체들은 데이터의 값에 영향을 받게 된다. 그것이 조건문으로 나타나든, 변수로만 나타나든 어떻게든 코드 상에 모습을 드러내게 된다. 이것은 다음과 같은 문제들을 발생시킨다.


코드가 복잡해진다

데이터를 가지고 있으면 데이터를 핸들링 해야 한다. 핸들링하는 코드는 단순히 전달하거나 저장하는데만 그치는 경우도 있고, 데이터 값의 범위에 따라서 조건문이나 제어문이 필요한 경우도 있다. 어떤 식으로든 코드가 늘어나면 문제가 생기는 것은 당연하다.


데이터의 변경에 다수의 객체가 영향을 받는다

일단 데이터를 가지고 있으면 더이상 쓸모없는 데이터여서 지우거나, 새로운 데이터가 더해지거나 데이터의 타입이 변경되는 등의 여러가지 변경사항에 영향을 받게 된다. 이는 OCP(Open Close Principle) 원칙을 위배하게 된다.


데이터의 무결성을 지키기 어렵다 

각 객체들이 가지고 있는 데이터 값이 시간에 따라서 달라짐으로써 관리가 어려워지게 된다. 특히 멀티 쓰레드 환경에서 여러 곳에 데이터가 흩어져 있으면 데이터의 무결성을 지키기는 더더욱 어렵고 복잡해진다.


중복 코드가 발생할 가능성이 높다

한가지 데이터는 보통 소프트웨어 전체에서 한가지 용도로 사용된다. 따라서 하나의 데이터를 다루는 코드들은 유사성이 매우 높다. 이 코드들은 애초에 한번만 작성되도록 만들어져야 하는데 데이터가 여러 객체로 전달되고 나면 중복 코드가 발생하는 것은 거의 필연적이다.


그렇다면 어떻게 이 문제를 해결할 수 있는지 한번 살펴보자.


간단히 이야기 하자면, 데이터가 입력된 이후에는 데이터를 핸들링하는 객체를 별도로 생성하여 관리하면 된다. 위 그림에서는 데이터 객체가 이에 해당한다. 객체1은 데이터를 받아서 데이터 객체를 생성하거나 데이터 객체에 전달해 주는 것으로 자기 일을 마친다. 그리고 기존에 데이터를 가지고 다루던 객체들은 모두 데이터 객체에게 일을 시키는( tell() ) 형태로 설계를 변경한다. 그러면 데이터와 관련된 모든 일은 데이터 객체가 수행하게 되면서 다른 객체들이 데이터에 의존하는 것을 막을 수 있다.

이를 단계별로 설명해보면 다음과 같다.


데이터를 수신(생성) 단계

소프트웨어의 어떤 부분이든 데이터를 수신하거나 생성해 낸 곳이 있기 마련이다. 데이터는 발생 시점부터가 중요하다. 데이터를 최초로 수신한 객체는 일단 다른 곳으로 데이터를 전파시킬 수 없어야 한다. getter를 통해서 다른 객체들이 데이터를 가지고 가게 하거나, 다른 객체의 매개변수로 데이터를 전송해서는 안된다.


데이터 객체 생성 단계

데이터를 처리할 객체를 생성한다. 데이터는 생성된 이후에는 오직 이 객체에게만 전달된다. 데이터를 처리하는 방식은 두가지가 있다.

1. 데이터 처리를 전담하는 객체가 있다. 이 경우라면 최초로 데이터를 수신한 객체는 처리 객체에 데이터를 넘겨주기만 하면 된다.

dataProcessor.receiveData(data);

2. 데이터 처리를 전담하는 객체가 없고, 여러 객체들이 데이터에 대한 의존성을 가지고 있는 경우가 있다. 이런 경우에는 데이터 객체를 (필요시) 생성하고, 이 데이터 객체를 다른 객체에 전달하여 준다. 이 객체는 데이터가 변경되었을 때 다른 객체를 어떻게 변경시켜야 하는지를 알고 있다. 이렇게 데이터를 객체화 하여 전달하는 것은 State 패턴이나 Strategy 패턴과 유사한 모양이 된다.

// 데이터 객체가 생성된다

Data dataObject = new Data(data);

// 처리를 위해 다른 객체에 전달된다.

object.receiveData(dataObject);


데이터 처리 단계

데이터 객체 생성 단계에서 방법이 두가지가 있듯이 처리 방식도 두가지이다.

1. 데이터 처리 전담 객체의 경우에는 다른 객체들이 수시로 데이터 갱신이 이루어 졌는지를 데이터 처리 전담 객체에게 물어보는 방식이 있고, 다른 객체들이 데이터 처리 객체에 자신을 이벤트 리시버로 등록하는 경우가 있다. 후자는 Observer 패턴과 유사하다.

2. 데이터 객체가 생성되서 다른 객체로 전송되어 오면 각 객체들은 이 데이터 객체를 이용하여 변경된 데이터에 의한 동작을 수행해야 한다. 이 때 각 객체들은 데이터를 모르기 때문에 직접 자기 자신의 행위를 변경할 수는 없다. 따라서 데이터 객체에게 자신의 상태를 변경해 달라고 요청해야 한다. 따라서 이를 실행하면 아래와 같은 형식의 코드가 된다.

// 데이터 객체를 수신한 쪽 : Object2라고 가정했을 때

dataObject.doubleDispatch(this);


// 데이터 객체 쪽

public void doubleDispatch(Object2 object){

    object.doSomething();

}

이러한 방식을 켄트 벡의 구현 패턴에서는 "더블 디스패치"라고 한다. 데이터를 알고 있는 쪽에서 데이터에 종속적으로 동작하는 객체를 넘겨 받아서 자기가 알고 있는 데이터를 기반으로 넘겨 받은 객체의 행위를 호출하는 것이다. 이렇게 하면 데이터를 넘겨주지 않고도 데이터가 넘어 갔을 때 일어나야 하는 행위를 호출할 수 있게 된다.


"정보"의 전달을 금지하는 원칙

기본적으로 정보 은닉(information hiding)은 단순히 캡슐화만을 의미하는 것이 아니다. 정보 은닉(information hiding)에 대한 올바른 이해에서도 이야기 했듯이 생성된 객체의 구체적인 타입을 숨기는 것이나 구현을 숨기는 것도 정보 은닉에 해당된다. 또한 아무리 캡슐화를 잘 했다고 해도 getter를 통해서 데이터를 전달하거나 매개변수로 데이터를 다른 객체에 넘겨버리면 기껏 정보 은닉을 강조한 보람이 없어진다.

정보 은닉은 데이터의 종류를 막론하고 데이터 처리를 수행하는 전담 객체가 아니면 어떠한 객체도 데이터를 전달해주지 않아야 한다는 원칙으로 해석해야 한다. 이 정보에는 외부에서 받은 데이터도 포함되지만 생성한 객체의 구체적인 타입이나 구현부와 같이 프로그래밍 요소의 정보도 포함이 된다. 그리고 이런 정보들은 생성과 동시에 은닉 됨으로써 정보에 의존하는 코드들의 생성을 막아야 한다. 이것이 Tell, don't ask 원칙과 정보 은닉 원칙이 추구하는 방향이다.

Posted by 이세영2
,