07 객체 분해
이전 장까지는 객체지향이 무엇인지 또 어떻게 해야 객체지향적으로 설계를 할 수 있을지에 대해 이야기를 했다면 '7장 객체분해'에서는 객체지향의 본질에 대해 설명하는 챕터였습니다. 7장을 읽기 이전까지는 객체간의 의미있는 협력을 하게끔 유도 하는 것 == 객체지향이라 생각을 하였습니다. 그러나 7장에서는 '데이터 추상화'를 한 것이라 언급을 하며 객체지향이 탄생하기 까지 어떤 일들이 있었는지 이야기를 하며 이야기를 이끌어 나갔습니다.
이처럼 불필요한 정보를 제거하고 현재의 문제 해결에 필요한 정보를 제거하고 현재의 문제 해결에 필요한 핵심만 남기는 작업을 추상화라고 한다.
...
큰 문제를 해결 가능한 작은 문제로 나누는 작업을 분해라고 부른다.
객체지향을 이야기하기 이전 객체지향이 주로 다루는 '추상화'에 대한 정의와 설계를 할 때 중요한 작업중 하나인 '분해'에 대해 이야기를 하고 있습니다.
01 프로시저 추상화와 데이터 추상화
현대적인 프로그래밍 언어를 특징 짓는 중요한 추상화 메커니즘은 프로시저 추상화와 데이터 추상화다. 프로시저 추상화는 소프트웨어가 무엇을 해야 하는지를 추상화한다.
...
프로시저 추상화를 중심으로 시스템을 분해하기로 결정했다면 기능 분해(알고리즘 분해)의 길로 들어서는 것이다.
...
데이터 추상화를 중심으로 시스템을 분해하기로 결정했다면 다시 두 가지 중 하나를 선택해야 한다. 하나는 데이터를 중심으로 타입을 추상화하는 것이고 다른 하나는 데이터를 중심으로 프로시저를 추상화하는 것이다. 전자를 추상 데이터 타입이라고 부르고 후자를 객체지향이라고 부른다.
챕터를 전부 읽고 이해가 가질 않는 부분을 반복해서 읽어도 한번에 와닿지 않는 대목입니다. 후술하겠지만 먼저 개요를 이야기하자면 프로시저 추상화는 탑다운(top-down)방식으로 설계를 하는 것을 의미하고 데이터 추상화는 객체지향(프로시저 추상화) 객체기반(타입을 추상화)라고 할 수 있습니다.
02 프로시저 추상화와 기능 분해
메인 함수로서의 시스템
프로시저는 반복적으로 실행되거나 거의 유사하게 실행되는 작업들을 하나의 장소에 모아놓음으로써 로직을 재사용하고 중복을 방지할 수 있는 추상화 방법이다.
...
기능 분해 방법에서는 기능을 중심으로 필요한 데이터를 결정한다. 기능 분해라는 무대의 주연은 기능이며 데이터는 기능을 보조하는 조연의 역할에 머무른다.
...
하향식 기능 분해는 논리적이고 체계적인 시스템 개발 절차를 제시한다.
...
문제는 우리가 사는 세계는 그렇게 체계적이지도, 이상적이지도 않다는 점이다.
이후 모듈이라는 이름으로 한번 더 나오지만 라이브러리와 비슷한 느낌이라 보아도 될 듯합니다. 또한 프로시저 추상화를 구현할때는 보통 하향식으로 기능을 분해하여 프로그램을 설계하는 데 이는 이상적인 설계 방법이지만 현실세계와의 매칭이 잘 되지 않기 때문에 실제로는 여러 문제점이 발생하곤 합니다. 그렇기에 프로시저 추상화는 알고리즘분석 혹은 변경 가능성이 적은 정적인 프로그램을 설명하는데 주로 사용되곤 합니다.
하향식 기능 분해의 문제점
하나의 메인 함수라는 비현실적인 아이디어
하향식 접근접은 하나의 알고리즘을 구현하거나 배치 처리를 구현하기에는 적합하지만 현대적인 상호작용 시스템을 개발하는 데는 적합하지 않다.
하향식 알고리즘은 정적인 프로그램을 설명하거나 혹은 기술 문서를 통해 누군가에게 설명할때에는 좋은 방법중 하나이지만 설계를 할 때에는 모든 것을 알고 있는 상태에서 설계를 해야하고 인터페이스와 내부 구현 로직이 강하게 결합되기 프로그램을 설계할때는 좋은 방법이 아닙니다.
비즈니스 로직과 사용자 인터페이스의 결합
하향식 접근법은 비즈니스 로직을 설계하는 초기 단계부터 입력 방법부터 입력 방법과 출력 양식을 함께 고민하도록 강요한다.
...
결과적으로 코드 안에서 비즈니스 로직과 사용자 인터페이스 로직이 밀접하게 결합된다.
현대적인 프로그램은 변경이 자주 일어날수 밖에 없습니다. 그리고 이러한 프로그램은 이전 장에서 이야기한 구현을 설계 단계에서 미리 정하고 개발할 경우 개발의 난이도는 급격하게 높아지며 설계의 품질도 떨어지게 됩니다.
성급하게 결정된 실행 순서
설계를 시작하는 시점부터 시스템이 무엇(What)을 해야 하는지가 아니라 어떻게(How) 동작해야 하는지에 집중하도록 만든다.
...
하향식 접근법의 실체는 처음부터 구현을 염두에 두기 때문에 자연스럽게 함수들의 실행 순서를 정의하는 시간 제약(temporal constrant)을 강조하낟.
...
기능 분해 방식은 중앙집중 제어 스타일의 형태를 띨 수밖에 없다.
이 모든 것은 프로그램이 어떻게 동작하는지를 설계할 때부터 생각을 해야 하기 때문에 벌어지는 문제점이라 생각합니다. 그리고 시간 제약을 받는 코드가 많아질수록 유지보수가 어려워 진다 생각합니다.
데이터 변경으로 인한 파급효과
하향식 기능 분해의 가장 큰 문제점은 어떤 데이터를 어떤 함수가 사용하고 있는지를 추적하기 어렵다는 것이다.
...
이것은 코드 안에서 텍스트를 검색하는 단순한 문제가 아니다. 이것은 의존성과 결합도의 문제다.
...
이것이 바로 의존성 관리의 핵심이다. 변경에 대한 영향을 최소화하기 위해 영향을 받는 부분과 받지 않는 부분을 명확하게 분리하고 잘 정의된 퍼블릭 인터페이스를 통해 변경되는 부분에 대한 접근을 통제하라.
하향식 기능 분해를 하면 외부에 노출하는 인터페이스에 내부의 구현이 들어나기 쉽습니다. 또한 각 함수들이 사용하는 데이터가 명확하게 드러나지 않기에(이전장에 나왔던 용어로 치환해서 이야기하면 각 메서드간의 책임이 명확하지 않기 때문에) 부수 효과혹은 파급효과가 필연적으로 발생할 수 밖에 없다 생각합니다.
언제 하향식 분해가 유용한가?
하향식 분해 방식으로 설계된 소프트웨어는 하나의 함수에 제어가 집중되기 때문에 확장이 어렵다. 하향식 분해는 프로젝트 초기에 설계의 본질적인 측면을 무시하고 사용자 인터페이스 같은 비본질적인 측면에 집중하게 만든다. 과도하게 함수에 집중하게 함으로써 소프트웨어의 중요한 다른 측면인 데이터에 대한 영향도를 파악하기 어렵게 만든다. 또한 하향식 분해를 적용한 설계는 근본적으로 재사용하기 어렵다.
실제 우리가 사용하고 구현할 '동작하는 프로그램'을 구현할때는 하향식 분해는 좋지 못한 방법중 하나입니다. 그러나 작은 프로그램 그리고 개별적인 알고리즘을 설명하기 위해서는 유용한 패러다임입니다.
03모듈
정보 은닉과 모듈
정보 은닉은 시스템을 모듈 단위로 분해하기 위해 기본 원리로 시스템에서 자주 변경되는 부분을 상대적으로 덜 변경되는 안정적인 인터페이스 뒤로 감춰야 한다는 것이 핵심이다.
...
시스템을 모듈 단위로 어떻게 분해할 것인가? 시스템이 감춰야 하는 비밀을 찾아라. 외부에서 내부의비밀에 접근하지 못하도록 커다락 방어막을 쳐서 에워싸라. 이 방어막이 바로 퍼블릭 인터페이스가 된다.
...
복잡성 : 모듈이 너무 복잡한 경우 이해하고 사용하기가 어렵다. 외부에 모듈을 추상화할 수 있는 간단한 인터페이스를 제공해서 모듈의 복잡도를 낮춘다.
변경 가능성 : 변경 가능한 설계 결정이 외부에 노출될 경우 실제로 변경이 발생했을 때 파급효과가 커진다. 변경 발생 시 하나의 모듈만 수정하면 되도록 변경 가능한 설꼐 결정을 모듈 내부로 감추고 외부에는 쉽게 변경되지 않을 인터페이스를 제공한다.
개인적인 의견은 모듈을 일종의 라이브러리와 같이 생각을 해도 될것 같다는 생각이 들엇습니다. 또한 라이브러리와 달리 모듈은 개발자가 직접 구현해야 하기 때문에 모듈을 개발할때 주의해야 할 점 역시 이전 장에서 언급한 추상화를 생각하면 될 듯 하다는 생각이 들었습니다.
모듈의 장점과 한계
1. 모듈 내부의 변수가 변경되더라도 모듈 내부에만 영향을 미친다.
2. 비즈니스 로직과 사용자 인터페이스에 대한 관심사를 분리한다.
3. 전역 변수와 전역 함수를 제거함으러써 네임스페이스 오염을 방지한다.
...
모듈은 기능이 아니라 변경의 정도에 따라서 시스템을 분해하게 한다. 각 모듈은 외부에 감춰야 하는 비밀과 관련성 높은 데이터와 함수의 집합니다. 따라서 모듈 내부는 높은 응집도를 유지한다. 모듈과 모듈 사이에는 퍼블릭 인터페이스를 통해서만 통신해야 한다. 따라서 낮은 결합도륵 유지한다.
...
모듈의 가장 큰 단점은 인스턴스의 개념을제공하지 않는다는 점이다.
...
이를 만족시키기 위해 등장한 개념이 바로 추상 데이터 타입이다.
하향식 설계방식이 아닌 모듈을 사용하여 설계를 하였을때 얻을 수 있는 장점과 단점입니다. 사실 단점들은 단점이라기 보단 모듈의 본질적인 측면을 생각하면 당연하다는 생각이 듭니다. 또한 모듈을 '객체'라는 단어로 치환해도 어느정도 말이 된다 생각합니다.
04 데이터 추상화와 추상 데이터 타입
추상 데이터 타입
추상 데이터 타입을 구현하려면 다음과 같은 특성을 위한 프로그래밍 언어의 지원이 필요하다.
1. 타입 정의를 선언할 수 있어야 한다.
2. 타입의 인스턴스를 다루기 위해 사용할 수있는 오퍼레이션의 집합을 정의할 수 있어야 한다.
3. 제공된 오퍼레이션을 통해서만 조작할 수 있도록 데이터를 외부로부터 보호할 수 있어야 한다.
4. 타입에 대해 여러개의 인스턴스르 생성할 수 있어야 한다.
이야기만 들어보면 Class의 개념과 동일하지만 이는 올바른 사실이 아닙니다.
05 클래스
클래스는 추상 데이터 타입인가?
명확한 의미에서 추상 데이터 타입과 클래스는 동일하지 않다. 가장 핵심적인 차이는 클래스는 상속과 다형성을 지원하는데 비해 추상 데이터 타입은 지원하지 못한다는 점이다.
...
상속,다형성을 지원하는 객체지향 프로그래밍, 지원하지 않는 프로그래밍 패러다임은 객체기반 프로그래밍이라 한다.
...
윌리엄 쿡은 이처럼 하나의 대표적인 타입이 다수의 세부적인 타입을 감추기 때문에 이를 타입 추상화라고 불렀다.
...
타입 추상화를 기반으로 하는 대표적인 기법이 바로 추상 데이터 타입이다.
...
추상 데이터 타입은 오퍼레이션을 기준으로 타입을 묶는다.
...
추상 데이터 타입이 오퍼레이션을 기주ㅠ으로 타입을 묶는 방법이라면 객체지향은 타입을 기준으로 오퍼레이션을 묶는다.
...
내부에서 수생되는 절차는 다르지만 클래스를 이용한 다형성은 절차에 대한 차이점을 감춘다. 다시 말해 객체 지향은 절차 추상화이다.
...
객체지향은 타입을 기준으로 오퍼레이션을 묶는다.
...
추상 데이터 타입만을 지원하는 패러다임을 객체기반이라 하고 상속과 다형성을 지원하는 프로그래밍은 객체지향 프로그래밍이라고 하는 것이 핵심이라 생각합니다.
변경을 기준으로 선택하라
이처럼 인스턴스 변수에 저장된 값을 기반으로 메서드 내에서 타입을 명시적으로 구분하는 방식은 객체지향을 위반하는 것으로 간주된다.
...
객체지향에서는 타입 변수를 이용한 조건문을 다형성으로 대체한다.
...
기존 코드에 아무런 영향을 미치지 않고 새로운 객체 유형과 행위를 추가할 수 있는 객체지향의 특성을 개방-폐쇄 원칙(OCP)이라고 부른다.
...
새로운 타입을 빈번하게 추가해야 한다면 객체지향의 클래스 구조가 더 유용하다. 새로운 오퍼레이션을 빈번하게 추가해야 한다면 추상 데이터 타입을 선택하는 것이 현명한 판단이다. 변경의 축을 찾아라. 객체지향적인 접근법이 모든 경우에 올바른 해결 방법인것은 아니다.
이 대목은 이번 챕터에서 가장 중요하다가 느껴졌습니다. 컴퓨터 공학은 전공하면서 절차지향적인 언어를 배우고(C언어) 객체지향적인 언어(Java)를 배우며 세상에 존재하는 프로그램은 객체지향으로 대체할 수 있으며 객체지향 프로그래밍이 최고다! 라는 생각을 가지고 있었습니다. 그러나 객체지향 프로그래밍이 항상 답은 아니라는 것, 그리고 객체지향 언어르 사용하더라도 객체 내부에서 인스턴스 변수의 타입을 통해 분기 처리한다면 객체지향을 위반한다는 것은 저에게 신선한 충격으로 다가왔습니다. 그렇기에 향후 프로젝트를 진행할때 이를 조심할듯 합니다.
글을 처음 쓸때와 달리 지금은 프로시저 추상화라는 것이 대략적으로나 무슨 뜻인지 감이 잡히는 기분이 듭니다.