오브젝트 - 07. 객체 분해

강준혁·2022년 9월 15일
0

오브젝트

목록 보기
8/14
post-thumbnail

한번에 다뤄야 하는 정보의 수를 줄이기 위해 본질적 정보 외의 불필요한 세부사항을 걸러내면 문제를 단순화할 수 있다.
이를 추상화 라고 한다.

가장 일반적인 추상화 방법은 한번에 다뤄야 하는 문제의 크기를 줄이는 것이다.
이처럼 큰 문제를 해결가능한 작은 문제로 나누는 작업을 분해 라고 한다.

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

현대 프로그래밍 언어를 특정짓는 중요한 두 가지 추상화 메커니즘은 프로시저 추상화와 데이터 추상화 이다.

프로시저 추상화는 소프트웨어가 무엇을 해야 하는지를 추상화 한다.

데이터 추상화는 소프트웨어가 무엇을 알아야 하는지를 추상화 한다.

프로시저 추상화 중심으로 시스템을 분해하는 경우 기능분해 라고 한다.

데이터 추상화 중심으로 시스템을 분해하는 경우, 타입 추상화/프로시저 추상화 둘 중 하나의 방법을 선택할 수 있다.

타입 추상화 방식은 추상 데이터 타입 이라 하며 프로시저 추상화 방식을 객체지향 이라고 부른다.

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

하향식 접근법

전통적인 기능 분해 방법은 하향식 접근법을 따른다.

시스템을 구성하는 가장 최상위 기능을 정의하고 이를 더 작은 단계의 기능으로 분해해 나가는 방법을 말한다. 분해는 세분화된 마지막 하위 기능이 프로그래밍 언어로 구현 가능한 수준이 될 때까지 계속된다.

하향식 접근법의 문제점

  • 시스템은 하나의 메인 함수로 구성되어 있지 않다.
  • 기능 추가나 요구사항 변경으로 인해 메인 함수를 빈번히 수정해야 한다.
  • 비즈니스 로직이 사용자 인터페이스와 강하게 결합된다.
  • 하향식 분해는 너무 이른시기에 함수들의 실행순서를 고정시키기 때문에 유연성과 재사용성이 저하된다.
  • 데이터 형식이 변경될 경우 파급효과를 예측할 수 없다.

즉, 하향식 접근법은 변경에 취약한 설계를 낳는다.

하향식 분해는 작은 프로그램과 개별 알고리즘을 위해서는 유용한 패러다임으로 남아있다. 특히 이미 해결된 알고리즘을 문서화하고 서술하는 데는 훌륭한 기법이다.

하지만 실제로 동작하는 커다란 소프트웨어를 설계하는데 적합하지는 않다.

모듈

모듈은 서브프로그램이라기 보다는 책임의 할당이다. 모듈화는 개별 모듈에 대한 작업이 시작되기 전에 정해야 하는 설계 결정들을 포함한다.

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

  • 복잡성 : 외부에 모듈을 추상화할 수 있는 간단한 인터페이스를 제공해서 복잡도를 낮춘다.
  • 변경 가능성 : 변경 발생 시 하나의 모듈만 수정하면 되도록 변경 가능한 설계 결정을 모듈 내부로 감추고 외부에는 쉽게 변경되지 않을 인터페이스를 제공한다.

모듈의 장점과 한계

모듈의 장점은 다음과 같다.

  • 모듈 내부의 변수가 변경되더라도 내부에만 영향을 미친다
  • 비즈니스 로직과 사용자 인터페이스에 대한 관심사를 분리한다
  • 전역변수, 전역함수를 제거함으로써 네임스페이스 오염을 방지한다

모듈은 기능이 아니라 변경의 정도에 따라 시스템을 분해하게 한다.
각 모듈은 외부에 감춰야 하는 비밀과 관련성 높은 데이터와 함수의 집합이다.
따라서 모듈 내부는 높은 응집도를 유지한다.

모듈과 모듈 사이에는 퍼블릭 인터페이스를 통해서만 통신해야 한다.
따라서 낮은 결합도를 유지한다.

모듈의 가장 큰 단점은 인스턴스의 개념을 제공하지 않는다는 점이다.

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

과거에 사용되던 주된 추상화는 프로시저 추상화였다.
시간이 흐르면서 사람들은 프로시저 추상화로는 프로그램의 표현력을 향상시키는데 한계가 있다는 사실을 발견했다.

추상 데이터 타입은 추상 객체의 클래스를 정의한 것으로 추상 객체에 사용할 수 있는 오퍼레이션을 이용해 규정된다. 이것은 오퍼레이션을 이용해 추상 데이터 타입을 정의할 수 있음을 의미한다. 추상 데이터 객체를 사용할 때 프로그래머는 오직 객체가 외부에 제공하는 행위에만 관심을 가지며 행위가 구현되는 세부적인 사항에 대해서는 무시한다.

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

명확한 의미에서 추상 데이터 타입과 클래스는 동일하지 않다.
추상 데이터 타입은 타입을 추상화 한 것이며 클래스는 절차를 추상화한 것이다.

추상 데이터 타입은 다음과 같이 오퍼레이션을 기준으로 타입을 묶는다.

class Employee {
  employeeType;
  caculatePay() {
    if (this.employeeType == TypeA) {
       ...
    } else if (this.employeeType == TypeB) {
       ...           
    }
  }
}

객체지향은 타입을 기준으로 오퍼레이션을 묶는다.

abstract class Employee {
  abstract caculatePay()
}

class EmployeeA extends Employee {
  calculatePay() {
    ...
  }
}
  
class EmployeeB extends Employee {
  calculatePay() {
    ...
  }
}

추상 데이터 타입 대비 객체지향 프로그래밍의 장점

단순히 클래스를 구현단위로 사용한다는 것이 객체지향 프로그래밍을 한다는 것을 의미하지는 않는다.

클래스가 추상 데이터 타입의 개념을 따르는지 확인할 수 있는 가장 간단한 방법은 클래스 내부에 인스턴스의 타입을 표현하는 변수가 있는지를 살펴보는 것이다.

위의 추상 데이터 타입 예제에서는 employeeType 이라는 변수로 직원의 유형을 구분한다. 그러므로 클래스를 사용했더라도 이는 객체지향이 아닌 추상 데이터 타입의 개념을 따른 것이다.

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

모든 설계 문제가 그런 것 처럼 조건문을 사용하는 방식을 기피하는 이유 역시 변경 때문이다. 추상 데이터 타입을 기반으로 한 경우 타입이 추가되었을 때 관련 타입과 연관된 모든 조건문을 수정해야 한다.

반면 객체지향은 새로운 타입의 클래스를 상속계층에 추가하고 필요한 메서드를 오버라이딩 하면 된다. 즉, 시스템에 새로운 로직을 추가하기 위해 기존 코드를 수정할 필요가 없다.

이처럼 기존 코드에 영향을 미치지 않고 새로운 객체유형과 행위를 추가할 수 있는 특성을 개방 - 폐쇄원칙이라 한다.

변경을 기준으로 선택하라

항상 절차를 추상화하는 객체지향 설계방식을 따라야 하는가?
추상 데이터 타입은 모든 경우에 최악의 선택인가?

설계는 변경과 관련된 것이다.
설계의 유용성은 변경의 방향성과 발생 빈도에 따라 결정된다.

타입 추가라는 변경의 압력이 더 강한 경우에는 객체지향의 손을 들어줘야 한다.

변경의 주된 압력이 오퍼레이션을 추가하는 것이라면 추상 데이터 타입의 승리를 선언해야 한다.
객체지향의 경우 새로운 오퍼레이션 추가 시 상속계층에 속하는 모든 클래스를 수정해야 한다.

profile
백엔드 개발자

0개의 댓글