오브젝트 독서회 6회차 - 7장 객체 분해

어제 올리기로 했었는데 올리지 못했습니다.

7장은 지난 6챕터에서 언급된 대로 6회차에 6,7장을 @Vincent @freebear 님과 함께 했습니다.

이번 장도 재미있고 유익한 내용이 많았습니다.


P216

그러나 문제 해결에 필요한 요소의 수가 단기 기억의 용량을 초과하는 순간 문제 해결 능력은 급격하게 떨어지고 만다. 이런 현상을 인지 과부하(cognitive overload) 라고 부른다.

@vincent

P217

이처럼 불필요한 정보를 제거하고 현재의 문제 해결에 필요한 핵심만 남기는 작업을 추상화 라고 부른다.
가장 일반적인 추상화 방법은 한 번에 다뤄야 하는 문제의 크기를 줄이는 것이다. 사람들은 한 번에 해결하기 어려운 커다란 문제에 맞닥뜨릴 경우 해결 가능한 작은 문제로 나누는 경향이 있다. 이렇게 나눠진 문제들 역시 한 번에 해결하기 어려울 정도로 크다면 다시 더 작은 문제로 나눌 수 있다. 이처럼 큰 문제를 해결가능한 작은 문제로 나누는 작업을 분해(decomposition) 라고 부른다.

@vincent

어셈블리어는 숫자로 뒤범벅이 된 기계어에 인간이 이해할 수 있는 상징을 부여하려는 노력의 결과다. 고수준 언어는 기계적인 사고를 강요하는 낮은 수준의 명령어들을 탈피해서 인간의 눈높이에 맞는 기계 독립적이고 의미 있는 추상화를 제공하려는 시도의 결과였다.

@vincent

P218

따라서 모든 프로그래밍 패러다임은 추상화와 분해의 관점에서 설명할 수 있다.

@vincent

P220

여기서는 급여 관리 시스템을 구현하기 위해 기능 분해 방법을 이용하겠다. 전통적으로 기능 분해 방법은 하향식 접근법을 따르며 최상위의 추상적인 함수 정의에서 출발해서 단계적인 정제 절차를 따라 시스템을 구축한다. 이때 최상위의 추상적인 함수 정의는 시스템의 기능을 표현하는 하나의 문장으로 나타내고, 이 문장을 구성하는 좀 더 세부적인 단계의 문장으로 분해해 나가는 방식을 따른다. 기능 분해의 초점은 하나의 문장으로 표현된 기능을 여러 개의 더 작은 기능으로 분해하는 것이다.

@vincent

P226

설계는 코드 배치 방법이며 설계가 필요한 잉는 변경에 대비하기 위한 것이라는 점을 기억하라.

@freebear

먼저 하향식 접근법의 문제점에서 시작해서 기능 분해가 가지는 문제점을 순서대로 살펴보자.

@freebear

하향식 접근법은 하나의 알고리즘을 구현하거나 배치 처리를 구현하기에는 적합하지만 현대적인 상호작용 시스템을 개발하는 데는 적합하지 않다. 현대적인 시스템은 동등한 수준의 다양한 기능으로 구성된다. 버트란드 마이어의 말을 인용하자면 “실제 시스템에 정상(top)이란 존재하지 않는다[Meyer00].”

@vincent

P229

하향식 접근법은 사용자 인터페이스 로직과 비즈니스 로직을 한데 섞기 때문에 사용자 인터페이스를 변경하는 경우 비즈니스 로직까지 변경에 영향을 받게 된다. 따라서 하향식 접근법은 근본적으로 변경에 불안정한 아키텍쳐를 낳는다.

@vincent

P230

하향식 접근법의 설계는 처음부터 구현을 염두에 두기 떄문에 자연스럽게 함수들의 실행 순서를 정의하는 시간 제약(temporal constraint)을 강조한다.

@vincent

이를 해결할 수 있는 한 가지 방법은 자주 변경되는 시간적인 제약에 대한 미련을 버리고 좀 더 안정적인 논리적 제약(logical constraint)을 설계의 기준으로 삼는 것이다. 객체지향은 함수 간의 호출 순서가 아니라 객체 사이의 논리적인 관계를 중심으로 설계를 이끌어 나간다. 결과적으로 전체적인 시스템은 어떤 한 구성요소로 제어가 집중되지 않고 여러 객체들 사이로 제어 주체가 분산된다.

@vincent

재사용이라는 개념은 일반성이라는 의미를 포함한다는 점을 기억하라. 함수가 재사용 가능하려면 상위 함수보다 더 일반적이어야 한다. 하지만 하향식 접근법을 따를 경우 분해된 하위 함수는 항상 상위 함수보다 문맥에 더 종속적이다.

@vincent

P231

하향식 기능 분해의 가장 큰 문제점은 어떤 데이터를 어떤 함수가 사용하고 있는지를 추적하기 어렵다는 것이다. 따라서 데이터 변경으로 인해 어떤 함수가 영향을 받을지 예상하기 어렵다. 물론 개별 함수의 입장에서 사용하는 데이터를 파악하는 것은 어렵지 않다. 함수의 본체를 열어 참조하고 있는 모든 지역 변수, 인자, 전역 변수를 살펴보면 된다. 그러나 반대로 어떤 데이터가 어떤 함수에 의존하고 있는지를 파악하는 것은 어려운 일인데 모든 함수를 열어 데이터를 사용하고 있는지를 모두 확인해봐야 하기 떄문이다.

@vincent

P234

이것이 바로 의존성 관리의 핵심이다. 변경에 대한 영향을 최소화하기 위해 영향을 받는 부분과 받지 않는 부분을 명확하게 분리하고 잘 정의된 퍼블릭 인터페이스를 통해 변경되는 부분에 대한 접근을 통제하라.

@vincent

하향식은 이미 완전히 이해된 사실을 서술하기에 적합한 방법이다… 그러나 하향식은 새로운 것을 개발하고, 설계하고, 발견하는 데는 적합한 방법이 아니다. 이것은 수학과 아주 유사하다. 수학 교과서는 계산의 과정을 논리적인 순서로 서술한다.

@vincent

P235

하향식 분해는 작은 프로그램과 개별 알고리즘을 위해서는 유용한 패러다임으로 남아 있다. 특히 프로그래밍 과정에서 이미 해결된 알고리즘을 문서화하고 서술하는데는 훌륭한 기법이다. 그러나 실제로 동작하는 커다란 소프트웨어를 설계하는 데 적합한 방법은 아니다.

@vincent

P239

모듈의 한 가지 용도는 네임스페이스를 제공하는 것이다. 변수와 함수를 모듈 내부에 포함시키기 때문에 다른 모듈에서도 동일한 이름을 사용할 수 있게 된다. 따라서 모듈은 전역 네임스페이스의 오염을 방지하는 동시에 이름 충돌(name collision)의 위험을 완화한다.

@vincent

P240

비록 모듈이 프로시저 추상화보다는 높은 추상화 개념을 제공하지만 태생적으로 변경을 관리하기 위한 구현 기법이기 때문에 추상화 관점에서의 한계점이 명확하다.

@freebear

바바라 리스코프(Barbara Liskov)는 프로시저 추상화의 한계를 인지하고 대안을 탐색한 선각자 중 한명이다. 리스코프는 Programming with Abstract Data Types[Liskov74]에서 프로시저 추상화를 보완하기 위해 데이터 추상화(data abstraction) 의 개념을 제안했다.

@vincent

P241

추상 데이터 타입을 구현하려면 다음과 같은 특성을 위한 프로그래밍 언어의 지원이 필요하다.

  • 타입 정의를 선언할 수 있어야 한다.
  • 타입의 인스턴스를 다루기 위해 사용할 수 있는 오퍼레이션의 집합을 정의할 수 있어야 한다.
  • 제공된 오퍼레이션을 통해서만 조작할 수 있도록 데이터를 외부로부터 보호할 수 있어야 한다.
  • 타입에 대해 여러 개의 인스턴스를 생성할 수 있어야 한다.

@vincent

P245

그러나 명확한 의미에서 추상 데이터 타입과 클래스는 동일하지 않다. 가장 핵심적인 차이는 클래스는 상속과 다형성을 지원하는 데 비해 추상 데이터 타입은 지원하지 못한다는 점이다. 상속과 다형성을 지원하는 객체지향 프로그래밍(Object-Oriented Programming) 과 구분하기 위해 상속과 다형성을 지원하지 않는 추상 데이터 타입 기반의 프로그래밍 패러다임을 객체기반 프로그래밍(Object-Based Programming) 이라고 부르기도 한다.

@vincent

P251

이처럼 기존에 아무런 영향도 미치지 않고 새로운 객체 유형과 행위를 추가할 수 있는 객체지향의 특성을 개방-폐쇄 원칙(Open-Closed Principle, OCP)[Martin02] 이라고 부른다. 이것이 객체지향 설계가 전통적인 방식에 비해 변경하고 확장하기 쉬운 구조를 설계할 수 있는 이유다.

@freebear

설계는 변경과 관련된 것이다.

@freebear

그리고 추상 데이터 타입과 객체지향 설계의 유용성은 설계에 요구되는 변경의 압력이 '타입 추가’에 관한 것인지, 아니면 '오퍼레이션 추가’에 관한 것인지에 따라 달라진다.
타입 추가라는 변경의 압력이 더 강한 경우에는 객체지향의 손을 들어줘야 한다.

@vincent

이에 반해 변경의 주된 압력이 오퍼레이션을 추가하는 것이라면 추상 데이터 타입의 승리를 선언해야 한다.

@vincent

P252

객체를 설계하는 방법은 3장에서 설명했던 책임 주도 설계의 흐름을 따른다는 점을 기억하기 바란다.
객체가 참여할 협력을 결정하고 협력에 필요한 책임을 수행하기 위해 어떤 객체가 필요한지에 관해 고민하라. 그 책임을 다양한 방식으로 수행해야 할 때만 타입 계층 안에 각 절차를 추상화하라. 타입 계층과 다형성은 협력이라는 문맥 안에서 책임을 수행하는 방법에 관해 고민한 결과물이어야 하며 그 자체가 목적이 되어서는 안 된다.

@freebear

5개의 좋아요

이번 장은 6,7 장은 모두 읽고 함께 하기로 했으나 @freebear 님만 읽어오시고 저는 읽지 못하여, @freebear 님의 독후감문장을 받아 제가 7장을 마저 본 뒤에 추가했습니다.

이번 장에서는 추상화에 대한 정의가 다시 한번 나옵니다.

이처럼 불필요한 정보를 제거하고 현재의 문제 해결에 필요한 핵심만 남기는 작업을 추상화라고 부른다.

추상화란 객체지향의 전유물인줄 알았던 시절이 있었는데, 사실 이것을 함수형 프로그래밍을 공부하면서 “아 함수형도 추상화가 있고, 오히려 OOP보다 추상화 레이어가 하나 더 들어가는구나.” 하고 생각을 갖게되면서 추상화라는게 OOP만의 것은 아니라는 생각이 들었습니다.

추상화의 관점, 유지보수의 관점에서 소프트웨어를 바라보자고 관점이 확장된 것 같았습니다.


또한 하향식 접근법에 대한 내용도 나오는데,

하향식 접근법은 하나의 알고리즘을 구현하거나 배치 처리를 구현하기에는 적합하지만 현대적인 상호작용 시스템을 개발하는 데는 적합하지 않다. 현대적인 시스템은 동등한 수준의 다양한 기능으로 구성된다. 버트란드 마이어의 말을 인용하자면 “실제 시스템에 정상(top)이란 존재하지 않는다[Meyer00].”

이 내용도 무척 인상 깊었습니다.

저 또한 OOP를 누군가에게 설명할 때 객체지향시스템은 실세계와 많이 닮아있다;라고 설명을 하는 편이고, 객체도 사람같은 자아를 가진 능동형 생명체를 비유해서 설명합니다.

물론 저조차도 위와 같이 쓰지는 않습니다. 이제는 사실 위와 같은 OOP만을 지향한다면 예를 들어 IoC Container도 비즈니스를 표현하기 위한 객체는 아니기에 위 설명에 걸맞지는 않습니다. 저 조차도 누군가에게 하향식 접근법으로 OOP를 하라고 교육했던게 새삼 부끄러웠습니다.

시스템을 설계할 때 도메인을 잘 아는 사람들을 통해 비즈니스 로직을 만들고, 현세계에서 사용할 개념들을 가지고 클래스로 추상화하는 행위를 하는 것은 초보자의 입장에서 무척 당연한 것입니다. 그게 직관적이고 접근하기가 쉬우니까요. 그리고 비즈니스 모델만 정의를 하고서 OOP를 했다고 합니다.

하지만 저자는 진짜 OOP는 도메인 모델을 표현하기 위한 것이 아니라, 변경에 대한 유지보수가 용이한 방법론이라고 소개하고 있습니다.

변경

이라는 단어가 포인트인 것입니다. 변경에 대비하기 위해 아까 언급된 IoC Container같은 기법들과 다형성같은 기법들이 도입되는 것입니다.

현실 세계에서는 비즈니스에 변경이 가해지면 그냥 변경이 가해진 것이지만, 그것을 표현하는 소프트웨어 입장에서는 현실세계의 비즈니스가 변경된 것처럼 보이게 하는 OOP만의 방식을 따라야한다는 것입니다.

7장은 무척 재미있었습니다.

6개의 좋아요