7. 객체 분해
01. 프로시저 추상화와 데이터 추상화
소프트웨어는 데이터를 이용해 정보를 표현하고 프로시저를 이용해 대이터를 조작한다. 프로시저 추상화는 소프트웨어가 무엇을 해야 하는지를 추상화 하고 데이터 추상화는 소프트웨어가 무엇을 알아야 하는지 추상화 한다.
02. 프로시저 추상화와 기능분해
기능을 중심으로 시스템을 프로시저 단위로 추상화하여 분해하는 방식을 알고리즘 분해 또는 기능 분해라고 부른다.
프로시저
반복적으로 실행되거나 유사하게 실행되는 작업들을 하나의 장소에 모아놓음으로서 로직을 재사용하고 중복을 방지할수 있는 추상화 기법
인터페이스만 알면 사용가능하여 정보은닉 가능성을 제시하지만 한계가 있다.
기능 중심으로 시스템을 분해할 때는 하향식 접근방법을 따른다.
하향식 접근방법
- 시스템 구성하는 최상위 기능 정의. 좀더 작은 하위기능으로 분해.
- 마지막단계 : 프로그래밍 언어로 구현.
설계 예시 - 급여관리시스템
최상위 문장에서부터 점차 세부적인 절차로 구체화. 구현이 가능할정도의 저수준 문장이 될때까지 기능분해
직원의 급여를 계산한다
사용자로부터 소득세율을 입력받는다.
"세율을 입력하세요: "라는 문장을 화면에 출력한다
키보드를 통해 세율을 입력받는다
직원의 급여를 계산한다
전역변수에 저장된 직원의 기본급 정보를 얻는다
급여를 계산한다
양식에 맞게 결과를 출력한다
"이름: {직원명}, 급여: {계산된 금액}" 형식에 따라 출력 문자열을 생성한다.
'급여 관리 시스템'을 입력을 받아 출력을 생성하는 커다란 하나의 메인함수로 간주하고 기능 분해를 시작함. 기능 분해를 위한 하향식 접근법은 먼저 필요한 기능을 생각하고 이 기능을 분해하고 정제하는 과정에서 필요한 데이터의 종류와 저장방식을 식별한다.
설계 결과
하향식 기능 분해는 시스템을 최상위 가장 추상적인 메인함수로 정의하고, 메인 함수를 구현 가능한 수준까지 세부적인 단계로 분해하는 방법이다. 하향식 기능분해는 논리적이고 체계적인 시스템 개발절차를 제시한다. 설계가 어느 정도 안정화된 후에는 설계의 다양한 측면을 논리적으로 설명하기에 용이하다. 작은 프로그램과 개별 알고리즘을 위해서는 유용한 방법이다. 히행식 방식은 이미 완전히 이해된 사실을 서술하기에 좋은 방법 이다.
하향식 기능분해의 문제점
하나의 메인함수라는 비현실적 아이디어
시스템은 하나의 메인함수로 구성돼 있지 않다. 시간이 지나고 사용자를 만족시키기 위한 새로운 요구사항을 도출해나가면서 지속적으로 새로운 기능을 추가하게 된다. 새로운 기능들은 최초 메인함수의 일부분이 아닐 수 있다. 새로운 기능까지 포함하는 새로운 메인함수를 만드는 것은 생각보다 어렵다. 대부분의 시스템에서 하나의 메인기능이라는 존재하지 않는다.
메인 함수의 빈번한 재설계
기능 추가나 요구사항 변경으로 인해 메인함수를 빈번하게 수정해야할 상황이 생긴다. 특히 기존 로직과 별 상관 없는 기능을 추가할 때면 메인 함수의 구조를 변경해야 할 일이 생길 수 있다.
비즈니스 로직과 사용자 인터페이스의 결합
변경이 생기는 것은 당연하다. 하지만 문제는 비즈니스 로직과 사용자 인터페이스가 변경되는 빈도가 다르다는 것이다. 사용자 인터페이스 로직은 비즈니스 로직보다 자주 변경된다. 하향식 접근법에선 사용자 인터페이스 로직과 비즈니스 로직을 한데 섞기 때문에 사용자 인터페이스를 변경하는 경우 비즈니스로직까지 영향을 받게 된다.
성급하게 결정된 실행순서
하향식 분해는 너무 이른 시기에 함수들의 실행순서를 고정시키기 때문에 유연성과 재사용성이 저하된다.
데이터 변경으로 인한 파급효과
하향식 기능분해 방식에선 어떤 데이터를 어떤 함수가 사용하고 있는지 추적하기 어렵다. 따라서 데이터의 변경으로 인해 어떤 함수가 영향을 받을 지 예상하기 어렵다. 따라서 데이터 형식이 변경될 경우 파급효과를 예측할수 없다.
설계는 코드 배치방법이며 설계가 필요한 이유는 변경에 대비하기 위한 것이다. 하향식 접근법과 기능분해가 가지는 근본적인 문제점은 변경에 취약한 설계를 낳는다는 것! 하향식 접근법과 기능분해 방식에선 변경에 의한 파급효과를 예상하지 뭇한다. 함께 변경되는 부분을 하나의 구현단위로 묶고 퍼블릭 인터페이스로만 접근하도록 해야 한다.
모듈
정보은닉과 모듈
정보은닉이란 시스템을 모듈단위로 분해하기 위한 기본 원리로 시스템에서 자주 변경되는 부분을 상대적으로 덜 변경되는 안정적인 인터페이스 뒤로 감추는 것. 외부에 감춰야 하는 비밀에 따라 시스템을 분할하는 모듈 분할 원리.
모듈은 변경될 가능성이 있는 비밀을 내부로 감추고, 잘 정의되고 쉽게 변경되지 않을 퍼블릭 인터페이스를 외부에 제공해서 내부의 비밀에 함부러 접근하지 못하게 함. 기능이 아니라 데이터 중심으로 시수템을 분썩하는 것.
모듈은 다음과 같은 두 가지 비밀을 감춰야 한다.
- 복잡성
- 변경 가능성
모듈의 장점과 한계
장점
- 모듈 내부의 변수가 변경되더라도 모듈 내부에만 영향을 미친다.
- 비즈니스 로직과 사용자 인터페이스에 대한 관심사를 분리한다.
- 전역변수와 전역함수를 제거함으로서 namespace 오염을 방지한다.
- 각 모듈은 외부에 감춰야하는 비밀과 관련성 높은 데이통하 함수의 집합이기 때문에 모듈 내부는 높은 응집도를 유지한다.
한계
- 모듈은 단지 변경을 관리하기 위한 구현기법이기 때문에 인스턴스를 제공하지 않는다. 따라서 개별 데이터를 독립적으로 다루지 못한다.
04. 데이터 추상화와 추상 데이터타입
추상 데이터타입
추상 데이터타입은 추상객체의 클래스를 정의한 것으로 추상객체에 사용할 수 있는 오퍼레이션을 이용해 규정된다. 추상 데이터타입을 구현하려면 다음과같은 특성을 위한 프로그래밍언어의 지원이 필요하다.
- 타입 정의를 선언할 수 있어야 한다.
- 타입의 인스턴스를 다루기 위해 사용할 수 있는 오퍼레이션의 집합을 정의할 수 있어야 한다.
- 제공된 오퍼레이션을 통해서만 조작할수 있도록 데이터를 외부로부터 보호할 수 있어야 한다.
- 타입에대해 여러 개의 인스턴스를 생성할 수 있어야 한다.
모듈 개념을 사용하여 추상데이터타입을 흉내내는 정도는 가능함. 하지만 언어 차원에서 추상 데이터타입을 지원하는 것과는 다르다.
05. 클래스
클래스는 추상 데이터 타입인가?
추상 데이터 타입에 대해 알아봤는데 우리 머리 속엔 이런 의문이 떠오른다. '클래스는 추상 데이터 타입인가?'
비슷한 면이 있긴 하지만 두 개념이 동일하지는 않다.
- | 추상 데이터 타입 | 클래스 |
---|---|---|
상속과 다형성 | 지원 안함 | 지원 |
프로그래밍 패러다임 | 객체 기반 프로그래밍 | 객체 지향 프로그래밍 |
추상화 대상 | 타입을 추상화 | 절차를 추상화 |
구현 방법 | 오퍼레이션을 기준으로 타입을 묶음 | 타입 기준으로 오퍼레이션을 묶음 |
실제 코드를 통해 비교를 해 보면 둘의 차이를 알 수 있다. 추상 데이터 타입의 EmployType은 정규직원과 아르바이트 직원이라는 타입이 Employee 내부에 암묵적으로 감춰져 있지만 클래스는 명확하게 드러나 있다. 추상 데이터 타입은 메시지를 받으면 내부에서 분기문을 통해 로직이 실행되고, 클래스는 부모클래스를 구현한 자식 클래스가 무엇인지에 따라 다형적으로 메서드가 실행된다.
변경을 기준으로 선택하라
클래스를 구현 단위로 사용한다면 객체지향 프로그래밍을 하는 것인가? 그건 아니다. 타입을 기준으로 절차를 추상화하여야 한다. 또, 인스턴스 변수에 저장된 값을 기반으로 메서드 내에서 타입을 명시적으로 구분하는 방식은 객체지향을 위반하는 것으로 간주된다.
객체지향은 타입 변수를 이용한 조건문을 다형성으로 대체한다. 하지만 이것을 단순히 '객체지향이란 조건문을 제거하는 것'으로 받아들이면 안 된다. 그것보단 '객체지향이란 다형성을 사용함으로서 OCP를 만족시키고, 변경과 확장이 용이한 구조를 설계하는 것'으로 받아들여야 한다.
또한(지금 까지 계속 언급된것 처럼) 무조건 '추상 데이터 타입 보단 클래스를 사용해야 한다'라고 받아들여서도 안 된다. 타입 추가라는 변경의 압력이 높은 경우 객체지향의 손을 들어줘야 하지만 변경의 주된 압력이 오퍼레이션을 추가하는 것이라면 추상 데이터 타입을 사용하는것이 유리하다.
변경의 축을 찾아 적절한 방식을 사용해야 한다!