7. 객체 분해
- 문제 해결에 필요한 요소의 수가 단기 기억의 용량을 초과하는 순간 문제 해결 능력은 급격하게 떨어집니다. 이를 인지 과부하(cognitive overload) 라고 합니다.
- 불필요한 정보를 제거하고 현재의 문제 해결에 필요한 핵심만 남기는 작업을 추상화라고 합니다.
- 큰 문제를 해결 가능한 작은 문제로 나누는 작업을 분해(decomposition) 이라고 부릅니다.
#
01. 프로시저 추상화와 데이터 추상화- 현대적인 프로그래밍 언어를 특징 짓는 중요한 두 가지 추상화 메커니즘은 프로시저 추상화(procedure abstraction) 과 데이터 추상화(data abstraction) 입니다.
- 프로시저 추상화를 중심으로 시스템을 분해하기로 결정했다면 기능 분해(functional decomposition)의 길로 들어서는 것이며, 알고리즘 분해(algorithmic decomposition) 이라고 부릅니다.
- 데이터 충사화를 중심으로 시스템을 분해하기로 결정했다면 두 가지 중 하나를 선택해야 합니다.
- 데이터를 중심으로 타입을 추상화(type abstraction) 하는 것입니다. (추상 데이터 타입, Abstract Data Type)
- 데이터를 중심으로 프로시저를 추상화(procedure abstraction) 하는 것입니다. (객체지향, Object-Oriented)
#
02. 프로시저 추상화와 기능 분해#
메인 함수로서의 시스템- 시스템은 필요한 더 작은 작업으로 분해될 수 있는 하나의 커다란 메인 함수입니다.
- 전통적인 기능 분해 방법은 하향식 접근법(Top-Down Approach) 를 따릅니다.
#
급여 관리 시스템- 기능 분해의 결과는 최상위 기능을 수행하는 데 필요한 절차들을 실행되는 시간 순서에 따라 나열하는 것입니다.
- 기능 분해 방법에서는 기능을 중심으로 필요한 데이터를 결정합니다.
#
급여 관리 시스템 구현- 하향식 기능 분해는 논리적이고 체계적인 시스템 개발 절차를 제시합니다.
- 체계적이고 이상적인 방법이 불규칙하고 불완전한 인간과 만나는 지점에서 혼란과 동요가 발생합니다.
#
하향식 기능 분해의 문제점- 하향식 기능 분해 방법을 하다보면, 아래의 문제에 직면합니다.
- 시스템은 하나의 메인 함수로 구성돼 있지 않습니다.
- 기능 추가나 요구사항 변경으로 인해 메인 함수를 빈번하게 수정해야 합니다.
- 비즈니스 로직이 사용자 인터페이스와 강하게 결합됩니다.
- 하향식 분해는 너무 이른 시기에 함수들의 실행 순서를 고정시키기 때문에 유연성과 재사용성이 저하됩니다.
- 데이터 형식이 변경될 경우 파급효과를 예측할 수 없습니다.
- 하향식 접근법의 문제점에서 시작해서 기능 분해가 가지는 문제점은 다음과 같습니다.
#
하나의 메인 함수라는 비현실적인 아이디어- 대부분의 시스템에서 하나의 메인 기능이란 개념은 존재하지 않습니다.
- 모든 기능들은 규모라는 측면에서 차이가 있을 수는 있겠지만 가능성의 측면에서는 동등하게 독립적이고 완결된 하나의 기능을 표현합니다.
#
메인 함수의 빈번한 재설계- 시스템 안에는 여러 개의 정상이 존재하기 때문에 결과적으로 하나의 메인 함수를 유일한 정상으로 간주하는 하향성 기능 분해의 경우에는 새로운 기능을 추가할 때마다 매번 메인 함수를 수정해야 합니다.
- 결과적으로 기존 코드의 빈번한 수정으로 인한 버그 발생 확률이 높아지기 때문에 시스템은 변경에 취약해질 수밖에 없습니다.
#
비즈니스 로직과 사용자 인터페이스의 결합- 비즈니스 로직과 사용자 인터페이스가 변경되는 빈도가 다릅니다.
- 사용자 인터페이스의 관심사와 비즈니스 로직의 관심사를 동시에 고려하도록 강요하기 때문에 "관심사의 분리"라는 아키텍처 설계의 목적을 달성하기 어렵습니다.
#
성급하게 결정된 실행 순서- 하향식으로 기능을 분해하는 과정은 하나의 함수를 더 작은 함수로 분해하고, 분해된 함수들의 실행 순서를 결정하는 작업으로 요약할 수 있습니다.
- 하향식 접근법의 설계는 처음부터 구현을 염두에 두기 때문에 자연스럽게 함수들의 실행 순서를 정의하는 시간 제약을 강조합니다.
- 자주 변경되는 시간적인 제약에 대한 미련을 버리고 좀 더 안정적인 논리적 제약을 설계의 기준으로 삼습니다.
- 하향식 설계와 관련된 모든 문제의 원인은 결합도입니다.
#
데이터 변경으로 인한 파급효과- 하향식 기능 분해의 가장 큰 문제점은 어떤 데이터를 어떤 함수가 사용하고 있는지를 추적하기 어렵습니다.
- 데이터가 어떤 함수에 의존하고 있는지를 파악하는 것은 어려운 일인데 모든 함수를 열어 데이터를 사용하고 있는지를 모두 확인해봐야 하기 때문입니다.
- 의존성 관리의 핵심은 잘 정의된 퍼블릭 인터페이스를 통해 데이터에 대한 접근을 통제해야 합니다.
- 이에 대한 개념을 기반으로 정보 은닉과 모듈이라는 개념을 제시합니다.
#
언제 하향식 분해가 유용한가?- 하향식 분해는 프로젝트 초기에 설계의 본질적인 측면을 무시하고 사용자 인터페이스 같은 비본질적인 측면에 집중하게 만듭니다.
- 일반적으로 하향식 분해를 적용한 설계는 근본적으로 재사용하기 어렵습니다.
#
03. 모듈#
정보 은닉과 모듈- 정보 은닉(information hiding) 은 시스템을 모듈 단위로 분해하기 위한 기본 원리로 시스템에서 자주 변경되는 부분을 상대적으로 덜 변경되는 안정적인 인터페이스 뒤로 감춰야 한다는 것이 핵심입니다.
- 모듈은 다음과 같은 두 가지 비밀을 감춰야 합니다.
- 복잡성: 모듈이 너무 복잡한 경우 이해하고 사용하기가 어렵습니다. 외부에 모듈을 추상화할 수있는 간단한 인터페이스를 제공해서 모듈의 복잡도를 낮춥니다.
- 변경 가능성: 변경 가능한 설계 결정이 외부에 노출될 경우 실제로 변경이 발생했을 때 파급효과가 커집니다. 변경 발생 시 하나의 모듈만 수정하면 되도록 변경 가능한 설계 결정을 모듈 내부로 감추고 외부에는 쉽게 변경되지 않을 인터페이스를 제공합니다.
#
모듈의 장점과 한계- 모듈의 장점은 다음과 같습니다.
- 모듈 내부의 변수가 변경되더라도 모듈 내부에만 영향을 미칩니다.
- 비즈니스 로직과 사용자 인터페이스에 대한 관심사를 분리합니다.
- 전역 변수와 전역 함수를 제거함으로써 네임스페이스 오염(namespace pollution)을 방지합니다.
- 모듈이 프로시저 추상화보다는 높은 추상화 개념을 제공하지만 태생적으로 변경을 관리하기 위한 구현 기법이기 때문에 추상화 관점에서의 한계점이 명확합니다.
- 모듈의 가장 큰 단점은 인스턴스의 개념을 제공하지 않는다는 점입니다.
#
04. 데이터 추상화와 추상 데이터 타입#
추상 데이터 타입- 프로그래밍 언어에서 타입(type) 이란 변수에 저장할 수 있는 내용물의 종류와 변수에 적용될 수 있는 연산의 가짓수를 의미합니다.
- 추상 데이터 타입을 구현하려면 다음과 같은 특성을 위한 프로그래밍 언어의 지원이 필요합니다.
- 타입 정의를 선언할 수 있어야 합니다.
- 타입의 인스턴스를 다루기 위해 사용할 수 있는 오퍼레이션의 집합을 정의할 수 있어야 합니다.
- 제공된 오퍼레이션을 통해서만 조작할 수 있도록 데이터를 외부로부터 보호할 수 있어야 합니다.
- 타입에 대해 여러 개의 인스턴스를 생성할 수 있어야 합니다.
- 추상 데이터 타입의 기본 의도는 프로그래밍 언어가 제공하는 타입처럼 동작하는 사용자 정의 타입을 추가할 수 있게 하는 것입니다.
#
05. 클래스#
클래스는 추상 데이터 타입인가?- 명확한 의미에서 추상 데이터 타입과 클래스는 동일하지 않습니다.
- 추상 데이터 타입이 오퍼레이션을 기준으로 타입을 묶는 방법이라면 객체지향은 타입을 기준으로 오퍼레이션을 묶습니다.
- 객체 지향은 절차 추상화(procedural abstraction) 입니다.
- 추상 데이터 타입은 오퍼레이션을 기준으로 타입들을 추상화하며, 클래스는 타입을 기준으로 절차들을 추상화합니다. 이것이 추상화와 분해의 관점에서 추상 데이터 타입과 클래스의 다른 점입니다.
#
추상 데이터 타입에서 클래스로 변경하기- 메시지를 수신한 객체는 자신의 클래스에 구현된 메서드를 이용해 적절하게 반응할 수 있습니다.
#
변경을 기준으로 선택하라- 클래스를 구현 단위로 사용하는 것이 객체지향 프로그래밍을 한다는 것이 아닙니다.
- 클래스가 추상 데이터 타입의 개념을 따르는지를 확인할 수 있는 가장 간단한 방법은 클래스 내부에 인스턴스의 타입을 표현하는 변수가 있는지를 살펴보는 것입니다.
- 기존 코드에 아무런 영향을 미치지 않고 새로운 객체 유형과 행위를 추가할 수 있는 객체지향의 특성을 개방-폐쇄 원칙(Open-Closed Principl, OCP) 라고 부릅니다.
- 설계는 변경과 관련된 것입니다.
- 새로운 타입을 빈번하게 추가해야 한다면 객체지향의 클래스 구조가 더 유용합니다.
#
협력이 중요하다- 객체가 참여할 협력을 결정하고 협력에 필요한 책임을 수행하기 위해 어떤 객체가 필요한지에 관해 고민해야 합니다.