객체 분해

한 번에 다뤄야 하는 정보의 수를 줄이기 위해,
본질적인 정보를 남기고 불필요한 세부 사항을 걸러 문제를 단순화하자 ⇒ 추상화
01) 프로시저 추상화와 데이터 추상화
추상화 메커니즘
- 프로시저 추상화(procedure abstraction) : 소프트웨어가 무엇을 해야 하는지를 추상화한다.
- 기능 분해(functional decomposition) == 알고리즘 분해(algorithmic decomposition)
- 데이터 추상화(data abstraction) : 소프트웨어가 무엇을 알아야 하는지를 추상화한다.
- 데이터를 중심으로 타입을 추상화(type abstraction) == 추상 데이터 타입(Abstract Data Type)
- 데이터를 중심으로 프로시저를 추상화(procedure abstraction) == 객체지향(Object-Oriented)
소프트웨어는 데이터를 이용해 정보를 표현하고 프로시저를 이용해 데이터를 조작한다.
02) 프로시저 추상화와 기능 분해
프로시저란?
반복적으로 실행되거나 거의 유사하게 실행되는 작업들을 하나의 장소에 모아놓음으로써 로직을 재사용하고 중복을 방지할 수 있는 추상화 방법.
전통적인 기능 분해 방법은 하향식 접근법(Top-Down Approach)를 따른다. 가장 최상위 기능을 정의하고, 좀 더 작은 단계의 하위 기능으로 분해해 나가는 방법. 가장 상위 기능이 제일 추상적이고, 아래로 내려갈수록 구체적이다.
급여 관리 시스템 Top-Down
하향식 기능 분해의 문제점
- 시스템은 하나의 메인 함수로 구성돼 있지 않다.
- 새로운 요구 사항에 의해 계속해서 기능이 커지고 하나의 기능 이라는 것이 사라짐.
- 기능 추가나 요구사항 변경으로 인해 메인 함수를 빈번하게 수정해야 한다.
- 하향식 분해 기법의 경우 새로운 기능을 추가할 때마다 메인함수를 수정해야한다.
- 비즈니스 로직이 사용자 인터페이스와 강하게 결합된다.
- 사용자 인터페이스는 자주 수정되는 부분. 비즈니스 로직은 변경이 적게 발생한다.
- 따라서 사용자 인터페이스가 자주 변경되면, 하향식 접근법에서는 비즈니스 로직까지 덩달아 불안정한 아키텍처를 만든다.
- 하향식 분해는 너무 이른 시기에 함수들의 실행 순서를 고정시키기 때문에 유연성과 재사용성이 저하된다.
- 설계란, 무엇을 해야 하는 지가 아니라, 어떻게 동작해야 하는 지에 집중해야 한다.
- 그런데, 하향식 분해 방법은 분해를 먼저 해야하기 때문에, 실행 순서나 조건, 반복과 같은 제어 구조를 미리 결정하기 때문에 중앙집중 제어 스타일의 형태를 띄게 된다.
- 데이터 형식이 변경될 경우 파급효과를 예측할 수 없다.
- 하향식 기능 분해의 가장 큰 문제점은 어떤 데이터를 어떤 함수가 사용하고 있는 지를 추적하기 어렵다. 즉, 데이터가 변경된다면 어떤 함수가 영향을 받을 지 모른다.
- 데이터 변경으로 인한 영향을 최소화하려면, 데이터와 함께 변경되는 부분과 그렇지 않은 부분을 명확하게 분리해야 한다.
03) 모듈
모듈이란?
- 시스템의 변경을 관리하는 기본적인 전략은 함께 변경되는 부분을 하나의 구현 단위로 묶고 퍼블릭 인터페이스를 통해서만 접근하도록 만드는 것
- 기능을 기반으로 시스템을 분해하는 것이 아닌 변경의 방향에 맞춰 시스템을 분해하는 것이다.
모듈은 변경될 가능성이 있는 비밀을 내부로 감추고,
잘 정의되고 쉽게 변경되지 않을 퍼블릭 인터페이스를 외부에 제공해서 내부의 비밀에 함부로 접근하지 못하게 함.
시스템 → (분해) → 모듈 → (분해) → 기능 분해
모듈은 두 가지 비밀을 감추자!
- 복잡성
- 외부에 모듈을 추상화할 수 있는 간단한 인터페이스를 제공해서 모듈의 복잡도를 낮춘다.
- 변경 가능성
- 변경 발생 시 하나의 모듈만 수정하면 되도록 변경 가능한 설계 결정을 모듈 내부로 감추고 외부에는 쉽게 변경되지 않을 인터페이스를 제공한다.
모듈의 장점
- 모듈 내부의 변수가 변경 되더라도 모듈 내부에만 영향을 미친다.
- 비즈니스 로직과 사용자 인터페이스에 대한 관심사를 분리한다.
- 전역 변수와 전역 함수를 제거함으로써 네임스페이스 오염을 방지한다.
모듈의 한계
- 인스턴스의 개념을 제공하지 않는다는 점이다. 좀 더 높은 수준의 추상화를 위해서는 독립적인 단위로 다룰 수 있어야 한다. ⇒ 추상 데이터 타입
04) 데이터 추상화와 추상 데이터 타입
타입(type)이란?
변수에 저장할 수 있는 내용물의 종류와 변수에 적용될 수 있는 연산의 가짓수를 의미.
추상 데이터 타입은 프로시저 추상화 대신 데이터 추상화를 기반으로 소프트웨어를 개발.
추상 데이터 타입을 구현하려면…?
- 타입 정의를 선언할 수 있어야 한다.
- 타입의 인스턴스를 다루기 위해 사용할 수 있는 오퍼레이션의 집합을 정의할 수 있어야 한다.
- 제공된 오퍼레이션을 통해서만 조작할 수 있도록 데이터를 외부로부터 보호할 수 있어야 한다.
- 타입에 대해 여러 개의 인스턴스를 생성할 수 있어야 한다.
예를 들어, 직원에 대해 정의하기 위해 추상 데이터 타입을 이렇게 구현!
Employee = Struct.new(:name, :basePay, :hourly, :timecard)
사람들이 일반적으로 생각하는 (세상을 바라보는) 방식과 유사
05) 클래스
사실 클래스는 추상 데이터 타입과 다르다.
공통점
두 메커니즘 모두 외부에서는 객체의 내부 속성에 직접 접근할 수 없으며 오직 퍼블릭 인터페이스를 통해서만 외부와 의사소통할 수 있다.
차이점
클래스는 상속과 다형성을 지원하는 데 비해 추상 데이터 타입은 지원하지 못한다는 점이다.
클래스는 상속과 다형성을 지원하는 객체지향 프로그래밍(Object-Oriented Programming)
추상 데이터 타입 기반의 프로그래밍 패러다임을 객체기반 프로그래밍(Object-Based Programming)
변경을 기준으로 선택하라
단순히 클래스를 구현 단위로 사용한다는 것이 객체지향 프로그래밍을 의미하지는 않음.
타입을 기준으로 절차를 추상화하지 않았다? ⇒ 객체지향 분해가 아님.
클래스가 추상 데이터 타입의 개념을 따르는지를 확인할 수 있는 가장 간단한 방법은,
클래스 내부에 인스턴스의 타입을 표현하는 변수가 있는 지를 살펴보는 것.
⇒ 인스턴스 변수에 저장된 값을 기반으로 메서드 내에서 타입을 명시적으로 구분하는 방식은 객체 지향을 위반
새로운 타입을 빈번하게 추가한다? 객체지향의 클래스 구조
새로운 오퍼레이션을 빈번하게 추가한다? 추상 데이터 타입