jongfeel / Activity

Activity log of mentoring or study life
MIT License
1 stars 0 forks source link

오브젝트, CHAPTER 07 객체 분해, 2024-08-25 #455

Closed jongfeel closed 2 weeks ago

jongfeel commented 2 weeks ago

CHAPTER 07 객체 분해

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

큰 문제를 해결 가능한 작은 문제로 나누는 작업을 분해(decomposition)라고 부른다.

추상화와 분해는 인간이 세계를 인식하고 반응하기 위해 사용하는 가장 기본적인 사고 도구라고 할 수 있다. 인류가 창조한 가장 복잡한 분야의 문제인 소프트웨어 개발 영역은 추상화와 분해를 다루고 문제를 해결하기 위해 사용돼 왔다.

01 프로시저 추상화와 데이터 추상화

현대적인 프로그래밍 언어를 특징 짓는 중요한 두 가지 추상화 메커니즘은 프로시저 추상화(procedure abstraction)와 데이터 추상화(data abstraction)다. 프로시저 추상화는 소프트웨어가 무엇을 해야 하는지를 추상화한다. 데이터 추상화는 소프트웨어가 무엇을 알아야 하는지를 추상화한다. 소프트웨어는 데이터를 이용해 정보를 표현하고 프로시저를 이용해 데이터를 조작한다.

시스템을 분해하는 방법을 결정하려면 먼저 프로시저 추상화를 중심으로 할 것인지, 데이터 추상화를 중심으로 할 것인지를 결정해야 한다[Meyer00].

프로시저 추상화를 중심으로 시스템을 분해하기로 결정했다면 기능 분해(functional decomposition)의 길로 들어서는 것이다. 기능 분해는 알고리즘 분해(algorithmic decomposition)라고 부르기도 한다.

데이터 추상화를 중심으로 시스템을 분해하기로 결정했다면 다시 두 가지 중 하나를 선택해야 한다. 하나는 데이터를 중심으로 타입을 추상 화(type abstraction)하는 것이고 다른 하나는 데이터를 중심으로 프로시저를 추상화(procedure abstraction)하는 것이다[Cook90]. 전자를 추상 데이터 타입(Abstract Data Type)이라고 부르고 후자를 객체지향(Object-Oriented)이라고 부른다.

02 프로시저 추상화와 기능 분해

메인 함수로서의 시스템

기능은 오랜 시간 동안 시스템을 분해하기 위한 기준으로 사용됐으며, 이 같은 시스템 분해 방식을 알고리즘 분해 또는 기능 분해라고 부른다. 기능 분해의 관점에서 추상화의 단위는 프로시저이며 시스템은 프로시저를 단위로 분해된다.

전통적인 기능 분해 방법은 하향식 접근법(Top-Down Approach)을 따른다. 하향식 접근법이란 시스템을 구성하는 가장 최상위(topmost) 기능을 정의하고, 이 최상위 기능을 좀 더 작은 단계의 하위 기능으로 분해해 나가는 방법을 말한다. 분해는 세분화된 마지막 하위 기능이 프로그래밍 언어로 구현 가능한 수준이 될 때까지 계속된다.

급여 관리 시스템

기능 분해 방법에서는 기능을 중심으로 필요한 데이터를 결정한다. 기능 분해라는 무대의 주연은 기능 이며 데이터는 기능을 보조하는 조연의 역할에 머무른다. 기능이 우선이고 데이터는 기능의 뒤를 따른다. 기능 분해를 위한 하향식 접근법은 먼저 필요한 기능을 생각하고 이 기능을 분해하고 정제하는 과정에서 필요한 데이터의 종류와 저장 방식을 식별한다.

하향식 기능 분해 방식이 가지는 문제점을 이해하는 것은 유지보수 관점에서 객체지향의 장점을 이해할 수 있는 좋은 출발점이다. 소프트웨어 개발과 관련된 대부분의 기법이 그런 것처럼 기능 분해가 동작하는 방법과 그에 수반되는 문제점을 이해할 수 있는 가장 효과적인 방법은 실제 애플리케이션 코드를 살펴보는 것이다.

급여 관리 시스템 구현

하향식 기능 분해는 시스템을 최상위의 가장 추상적인 메인 함수로 정의하고, 메인 함수를 구현 가능한 수준까지 세부적인 단계로 분해하는 방법이다. 하향식 기능 분해 방식으로 설계한 시스템은 메인 함수를 루트로 하는 ‘트리(tree)’로 표현할 수 있다. 트리에서 각 노드(node) 는 시스템을 구성하는 하나의 프로시저를 의미하고 한 노드의 자식 노드는 부모 노드를 구현하는 절차 중의 한 단계를 의미한다.

하향식 기능 분해의 문제점

언제 하향식 분해가 유용한가?

하향식 아이디어가 매력적인 이유는 설계가 어느 정도 안정화된 후에는 설계의 다양한 측면을 논리적으로 설명하고 문서화하기에 용이하기 때문이다. 그러나 설계를 문서화하는 데 적절한 방법이 좋은 구조를 설계할 수 있는 방법과 동일한 것은 아니다.

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

03 모듈

정보 은닉과 모듈

데이비드 파나스는 1972년에 발표한 《On the Criteria To Be Used in Decomposing Systems Into Modules》[Parnas72]에서 소프트웨어 개발의 가장 중요한 원리인 동시에 가장 많은 오해를 받고 있는 정보 은닉(information hiding)의 개념을 소개했다. 정보 은닉은 시스템을 모듈 단위로 분해하기 위한 기본 원리로 시스템에서 자주 변경되는 부분을 상대적으로 덜 변경되는 안정적인 인터페이스 뒤로 감춰야 한다는 것이 핵심이다. 데이비드 파나스는 시스템을 모듈로 분할하는 원칙은 외부에 유출돼서는 안 되는 비밀의 윤곽을 따라야 한다고 주장한다.

모듈은 다음과 같은 두 가지 비밀을 감춰야 한다.

모듈의 장점과 한계

모듈의 장점

모듈은 데이터와 함수가 통합된 한 차원 높은 추상화를 제공하는 설계 단위다.

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

04 데이터 추상화와 추상 데이터 타입

추상 데이터 타입

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

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

추상 데이터 타입 정의를 기반으로 객체를 생성하는 것은 가능하지만 여전히 데이터와 기능을 분리해서 바라본다는 점에 주의하라. 추상 데이터 타입은 말 그대로 시스템의 상태를 저장할 데이터를 표현한다. 추상 데이터 타입으로 표현된 데이터를 이용해서 기능을 구현하는 핵심 로직은 추상 데이터 타입 외부에 존재한다.

05 클래스

클래스는 추상 데이터 타입인가?

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

윌리엄 쿡(William Cook)은 《Object-Oriented Programming Versus Abstract Data Types》 [Cook90]에서 객체지향과 추상 데이터 타입 간의 차이를 프로그래밍 언어적인 관점에서 설명한다. 쿡의 정의를 빌리자면 추상 데이터 타입은 타입을 추상화한 것(type abstraction)이고 클래스는 절차를 추상화한 것(procedural abstraction)이다.

추상 데이터 타입은 오퍼레이션을 기준으로 타입들을 추상화한다. 클래스는 타입을 기준으로 절차들을 추상화한다. 이것이 추상화와 분해의 관점에서 추상 데이터 타입과 클래스의 다른 점이다.

추상 데이터 타입에서 클래스로 변경하기

클래스를 이용하는 객체지향 버전에 서는 각 직원 타입을 독립적인 클래스로 구현함으로써 두 개의 타입이 존재한다는 사실을 명시적으로 표현한다. 결과적으로 클래스를 이용한 구현에서는 Employee 추상 데이터 타입에 구현돼 있던 타입별 코드가 두 개의 클래스로 분배된다.

변경을 기준으로 선택하라

객체지향에서는 타입 변수를 이용한 조건문을 다형성으로 대체한다. 클라이언트가 객체의 타입을 확인한 후 적절한 메서드를 호출하는 것이 아니라 객체가 메시지를 처리할 적절한 메서드를 선택한다.

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

새로운 타입을 빈번하게 추가해야 한다면 객체지향의 클래스 구조가 더 유용하다. 새로운 오퍼레이션을 빈번하게 추가해야 한다면 추상 데이터 타입을 선택하는 것이 현명한 판단이다.

협력이 중요하다

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