오브젝트: 코드로 이해하는 객체지향 설계 7장을 읽으며
오브젝트: 코드로 이해하는 객체지향 설계 7장을 읽으며
느낀점
데이터 타입 추상화와 클래스의 차이를 더 명확하게 이해할 수 있었음. 객체 분해가 단순히 구조를 나누는 것이 아니라, 필요에 따라 정보의 복잡도를 관리하는 중요한 과정. 복잡한 시스템에서 모듈화를 통해 인지 과부하를 줄이고 코드의 가독성을 높이는 데 큰 도움이 된다는 부분 고민
프로그래밍 언어의 설계에 있어 왜(why)에 대해서 더 잘 알게 되었달까?
객체 분해
개발자가 인지할 수 있는 용량을 초과하는 순간 인지과부하가 발생하여 문제 해결능력이 급격하게 떨어짐
- 이를 위해 정보의 양을 조절하는 것
- 불필요한 정보를 제거하고 현재의 문제 해결에 필요한 핵심만 남기는 것을 추상화라고 함
- 큰 문제를 작은 문제로 나누는 것을 분해(decomposition)이라고 함
프로시저 추상화와 데이터 추상화
엄세블리어는 숫자로 뒤범벅이 된 기계에어 인간이 이해할 수 있는 상징을 부여하려는 노력의 결과이며, 고수준 언어는 기계적인 사고를 강요하는 낮은 수준의 명령어들을 탈피해서 인간의 눈높이에 맞는 기계 독립적이고 의미있는 추상화를 제공하려는 시도의 결과
- 추상화 메커니즘은 프로시저 추상화와 데이터 추상화로 나눔
- 프로시저 추상화
- 소프트웨어가 무엇을 하는지를 추상화
- 기능 분해(functional decomposition)를 하는 것이며 기능 분해는 알고리즘 분해라고 부르기도 함
- 데이터 추상화
- 소프트웨어가 무엇을 알아야 하는지를 추상화
- 데이터 중심으로 타입을 추상화(as 추상 데이터 타입)하거나 프로시저를 추상화(as 객체 지향)하는 것 중 하나를 택해야 함
메인 함수로서의 시스템
기능과 데이터의 첫 번째 전쟁에서 신은 기능의 손을 들어주어 오랫동안 시스템을 분해하기 위한 기준으로 사용했으며, 알고리즘 분해 또는 기능 분해라고 부름
- 전통적인 기능 분해 방법은 하향식 접근법을 따름
- 기능 분해 방법으로는 기능을 중심으로 필요한 데이터를 결정하고, 필요한 데이터의 종류와 저장 방식을 식별함.
- 이는 유지보수에 다양한 문제를 야기함.
하향식 기능 분해의 문제점
하향식 기능 분해 방법은 겉으로는 이상적인 방법으로 보일 수 있지만 실제로 설계에 적용하다 보면 다양한 문제에 직면
- 전통적인 기능 분해 방법은 하향식 접근법을 따름
- 기능 분해 방법으로는 기능을 중심으로 필요한 데이터를 결정하고, 필요한 데이터의 종류와 저장 방식을 식별함.
하나의 메인 함수라는 비현실적인 아이디어
- 최초에 배포된 메인 함수의 일부가 아닐 것
- 대부분 시스템에서 하나의 메인 기능이란 개념은 존재하지 않음
메인 함수의 빈번한 재설계
- 하나의 메인 함수를 유일한 정상으로 간주하는 하향식 기능 분해
- 기존 코드를 수정하는 것은 항상 새로운 버그를 만들어낼 확률을 높인다는 것에 주의
비즈니스 로직과 사용자 인터페이스의 결합
- 하향식 접근법은 사용자 인터페이스 로직과 비즈니스 로직을 한데 섞기 때문에 사용자 인터페이스를 변경하는 경우 비즈니스 로직까지 변경에 영향을 받게 됨
- 하향식 접근법은 근본적으로 변경에 따라 불안정한 아키텍처를 낳음.
성급하게 결정된 실행 순서
- 하나의 함수를 더 작은 함수로 분해하고, 시스템이 무엇을 해야 하는지가 아니라 어떻게 동작해야 하는지에 집중하도록 만듦
- 하향식 접근법의 설계는 처음부터 실행 순서를 정의하는 시작 제약(temporal constraint)를 강조
- 제어 흐름의 결정이 상위 함수에서 이뤄지고 하위 함수는 상위 함수의 흐름에 따라 적절한 시점에 호출
- 하향식 접근법을 통해 분해한 함수들은 재사용하기도 어려움
- 하향식 접근법을 따른 경우 분해된 하위 함수는 항상 상위 함수보다 문맥에 더 종속적임
- 하향식 설계의 모든 근본적은 문제의 원인은 결합도임.
데이터 변경으로 인한 파급효과
- 모든 함수를 열어 데이터를 사용하고 있는지를 모두 확인해야 함.
- 의존성과 결합도의 문제임.
- 변경에 대한 영향을 최소화 하기 위해서는 영향을 받는 부분과 받지 않는 부분을 명확하게 분리하고 잘 정의된 퍼블릭 인터페이스를 통해 변경되는 부분에 대한 접근을 통제 해야함.
- 기능분해가 가진 근본적인 문제를 해결하고자 정보 은닉과 모듈이라는 개념 제시
언제 하향식 분해가 유용한가?
설계가 어느 정도 안정화 된 후에는 설계의 다양한 측면을 논리적으로 설명하고 문서화하기에 용이하기 때문에 매력적임
- 하향식 분해는 프로젝트 초기에 설계의 본질적인 측면을 무시하고 사용자 인터페이스와 같은 비본질적 측면에 집중하게 만듦
- 과도하게 함수에 집중하게 함으로써 소프트웨어의 중요한 다른 측면인 데이터에 대한 영향도를 파악하기 어렵게 만듦
- 하향식 분해를 적용한 설계는 근본적으로 재사용하기 어려움.
정보 은닉과 모듈
가장 중요한 원리인 동시에 가장 많은 오해를 받고 있는 정보 은닉(information hiding)
- 시스템을 모듈 단위로 분해하기 위한 기본 원리로 시스템에서 자주 변경되는 부분을 상대적으로 덜 변경되는 안정적인 인터페이스 뒤로 감춰야 한다는 것이 핵심
- 모듈과 기능 분해는 상호 배타적인 관계가 아님. 모듈은 다음과 같은 두 가지 비밀을 감춰야 함
- 복잡성: 모듈이 너무 복잡한 경우 이해하고 사용하기 어려움. 외부에 모듈을 추상화할 수 있는 간단한 인터페이스를 제공해서 모듈의 복잡도 낮춤
- 변경 가능성: 변경 가능한 설계 결정이 외부에 노출될 경우 실제로 변경이 발생했을 때 파급효과가 커짐. 변경 발생 시 하나의 모듈만 수정하면 되도록 변경 가능한 설계 결정을 모듈 내부로 감추고 외부에는 쉽게 변경되지 않을 인터페이스 제공
모듈이란? 키워드의 지원 여부와 상관없이 적용할 수 있는 논리적 개념
- C 언어는 파일 단위로 모듈을 관리하는 대표적인 언어. 이 중에서 외부에 공개할 부분은 external로 선언.
- 자바에서는 모듈 개념은 package를 이용해 구현 가능하며, C++과 C#에서는 namespace 사용
모듈의 장점과 한계
모듈은 기능이 아니라 변경의 정도에 따라 시스템을 분해
- 모듈 내부의 변수가 변경되더라도 모듈 내부에만 영향을 미침
- 비즈니스 로직과 사용자 인터페이스에 대한 관심사 분리
- 전역 변수와 전역 함수를 제거함으로써 네임스페이스 오염을 방지
- 모듈 내부는 높은 응집도를 유지하고 따라서 낮은 결합도를 유지
- 모듈의 가장 큰 단점은 인스턴스 개념을 제공하지 않는다는 것
- 즉, 개별 데이터를 독립적인 단위로 다룰 수 있어야 함. 추상화 메커니즘이 필요
추상 데이터 타입
프로그래밍 언어에서 타입(type)이란 변수에 저장할 수 있는 내용물의 종류와 변수에 적용될 수 있는 연산 가짓수 의미
- 프로그래밍 언어는 다양한 형태의 내장 타입(built-in type)을 제공
- 절차형 언어들은 적은 수의 내장 타입만을 제공
- 프로시저 추상화로는 프로그램의 표현력을 향상시키는데 한계가 있다는 사실 발경
- 리스코프는 프로시저 추상화의 한계를 인지하고 대안을 탐색한 선각자 중 한명.
- 프로시저 추상화를 보완하기 위해 데이터 추상화(data abstraction)개념을 제안
- 추상 데이터 타입을 구현하려면 프로그래밍 언어의 지원 필요
- 타입 정의할 수 있어야 함
- 타입의 인스턴스를 다루기 위해 사용할 수 있는 오퍼레이션의 집합을 정의할 수 있어야 함
- 제공된 오퍼레이션을 통해서만 조작할 수 있도록 외부로부터 보호할 수 있어야 함
- 타입에 대해 여러개의 인스턴스를 생성할 수 있어야 함.
루비는 추상 데이터 타입을 흉내 낼 수 있는 Struct라는 구성 요소를 제공
- 추상 데이터 타입에 정의된 오퍼레이션의 시그니처가 더 간단함.
- 추상 데이터 타입 정의를 기반을 ㅗ객체를 생성하는 것은 가능하지만 여전히 데이터와 기능을 분리해서 바라본다는 점을 주의
- 추상 데이터 타입은 말 그대로 시스템의 상태를 저장할 데이터를 표현
- 프로그래밍 언어 관점에서 추상 데이터 타입은 프로그래밍 언어의 내장 데이터 타입과 동일
클래스는 추상 데이터 타입인가?
대부분의 프로그래밍 서적은 추상 데이터 타입이라고 설명. 클래스와 추상 데이터 타입 모두 데이터 추상화를 기반으로 시스템을 분해하기 때문에 꼭 틀린건 아님
- 명확한 의미에서 추상데이터 타입과 클래스는 동일하지 않음.
- 상속과 다형성을 지원하는 객체지향 프로그래밍과 구분하기 위해 상속과 다형성을 지원하지 않는 추상 데이터 타입 기반의 프로그래밍 패러다임을 객체지향 프로그래밍이라고 함
- 추상 데이터 타입은 오퍼레이션을 기준으로 타입을 묶음
- 객체지향은 타입을 기준으로 오퍼레이션을 묶음
- 객체지향은 절차 추상화(procedural abstraction)임.
변경을 기준으로 선택하라
객체지향에서는 타입 변수를 이용한 조건문을 다형성으로 대체
- 흔히 '객체지향이란 조건문을 제거하는 것'이라는 다소 편협한 견해가 널리 퍼진 이유가 바로 이 때문.
- 이것은 시스템에 새로운 로직을 추가하기 위해 클라이언트 코드를 수정할 필요가 없다는 것을 의미
- 이처럼 기존 코드에 아무런 영향도 미치지 않고 새로운 객체 유형과 행위를 추가할 수 있는 객체지향의 특성을 개방-폐쇄 원칙(Open-Closed Principle, OCP)이라고 함
- 대부분의 객체지향 서적에서는 추상 데이터 타입을 기반으로 애플리케이션을 설계하는 방식을 잘못된 것으로 설명
- 설계의 유용성은 변경의 방향성과 발생 빈도에 따라 결정