Skip to main content

4. 컴포넌트 원칙

SOLID 원칙이 벽과 방에 벽돌을 배치하는 방법을 아려준다면, 컴포넌트 원칙은 빌딩에 방을 배치하는 방법을 알려줍니다.

12장. 컴포넌트#

  • 컴포넌트는 배포 단위며, 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위입니다.
    • 자바의 경우 jar 파일, 루비는 gem 파일, 닷넷은 DLL입니다.
    • 컴파일형 언어에서 컴포넌트는 바이너리 파일의 결합체입니다.
    • 인터프리터형 언어의 경우는 소스 파일의 결합체입니다.
  • 여러 머포넌트를 서로 링크하여 실행 가능한 단일 파일로 생성할 수 있습니다.
    • 여러 컴포넌트를 서로 묶어서 .war 파일과 같은 단일 아카이브로 만들 수 잇습니다.
    • 컴포넌트 각각을 .jar 이나 .dll 같이 동적으로 로드할 수 있는 플러그인이나 .exe 파일로 만들어서 독립적으로 배포할 수 있습니다.
    • 컴포넌트가 마지막에 어떤 형태로 배포되든, 잘 설계된 컴포넌트라면 반드시 독립적으로 배포 가능한, 독립적으로 개발 가능한 능력을 갖춰야 합니다.

컴포넌트의 간략한 역사#

  • 소프트웨어 개발 초창기에는 메모리에서의 프로그램 위치와 레이아웃을 프로그래머가 직접 제어했습니다.
    • 프로그램을 로드할 메모리의 위치를 정하는 일이 프로그래머가 가장 먼저 결정할 사항 중 하나 였습니다.
    • 장치가 느리고 메모리가 바싸서 자원이 한정적이였습니다.
  • 오늘날은 프로그램을 메모리의 어느 위치에 로드할지 고민할 필요가 거의 없습니다.

초기의 메모리 배치

  • 프로그램과 라이브러리가 사용하는 메모리가 늘수록 위의 단편화가 게속되므로 문제가 있었습니다.

재배치성#

  • 해결책은 재배치가 가능한 바이너리(relocatable binary) 였습니다.
    • 지능적인 로더를 사용해 메모리에 재배치할 수 있는 형태의 바이너리를 생성하도록 컴파일러를 수정하는 것입니다.
  • 프로그래머는 함수 라이브러리를 로드할 위치와 애플리케이션을 로드할 위치를 로더에게 지시할 수 있게 되었습니다.
    • 프로그래머는 오직 필요한 함수만을 로드할 수 있게 되었습니다.
  • 컴파일러는 재배치 가능한 바이너리 안의 함수 이름을 메타데이터 형태로 생성하도록 수정되었스빈다.
    • 만약 프로그램이 라이브러리 함수를 호출한다면 컴파일러는 라이브러리 함수 이름을 외부 참조(external reference) 로 생성했습니다.
    • 라이브러리 함수를 정의하는 프로그램이라면 이름을 외부 정의(external definition) 로 생성했습니다.
    • 이렇게 함으로 외부 정의를 로드할 위치가 정해지기만 하면 로더가 외부 참조를 외부 정의에 링크시킬 수 있게 되는 링킹 로더(linking loader) 가 탄생했습니다.

링커#

  • 링킹 로더의 등장으로 프로그래머는 프로그램을 개별적으로 컴파일하고 로드할 수 있는 단위로 분할할 수 있게 되었습니다.
  • 그러나 프로그램이 커지게되며 링킹 로더가 너무 느려지는 문제가 발생했습니다.
  • 이를 해결하기 위해 로드링크로 분리되었습니다.
    • 느린 링크 과정을 해결하기 위해 링커(linker) 라는 별도의 애플리케이션으로 이 작업을 처리하도록 만들었습니다.
    • 링커는 링크가 완료된 재배치 코드를 만들어 주었고, 그 덕분에 로더의 로딩 과정이 아주 빨라졌습니다.
  • 1980년대가 되며 C와 같은 고수준 언어를 사용하게 되며 프로그램이 더 커졌습니다.
  • 소스 모듈은 .c파일에서 .o 파일로 컴파일된 후, 링커로 전달되어 빠르게 로드될 수 있는 형태의 실행 파일로 만들어졌습니다.
    • 각 모듈은 컴파일하는 과정은 상대적으로 빨랐지만, 전체 모듈을 컴파일 하는 일은 꽤 시간이 걸렸습니다.
    • 로드 시간은 빨랐지만 컴파일-링크 시간이 병목 구간이 있었습니다.
  • 그 이후 디스크가 적어지고, 컴퓨터 메모리 또한 저렴해졌으며, 컴퓨터 클록 속도 또한 증가했습니다.
    • 1990년대 후반이 되자 프로그래머가 프로그램 성장시키는 속도보다 링크 시간이 줄어드는 속도가 더 빨라졌습니다.
  • 이후에는 .jar 파일도 등장했으며 컴퓨터와 장치가 빨려져서 로드와 링크를 동시에 할 수 있게 되었고 다수의 .jar 파일 또는 다수의 공유 라이브러리를 순식간에 서로 링크한 후, 링크가 끝난 프로그램을 실행할 수 있게 되었습니다.
    • 이렇게 컴포넌트 플러그인 아키텍처(component plugin architecture)가 탄생했습니다.
  • 오늘날은 .jar 파일, DLL, 공유 라이브러리를 기존 애플리케이션에 플러그인 형태로 배포하는 것이 일상적인 일이 되었습니다.

결론#

런타임에 플러그인 형태로 결합할 수 잇는 동적 링크 파일이 이 책에서 이야기하는 소프트웨어 컴포넌트에 해당합니다. 이 과정이 50년입니다.


13장. 컴포넌트 응집도#

컴포넌트 응집도에 관련된 세 가지 원칙이 있습니다.

  • REP: 재사용/릴리스 등가 원칙(Reuse/Release Equivalence Principle)
  • CCP: 공통 폐쇄 원칙(Common Closure Principle)
  • CRP: 공통 재사용 원칙(Common Reuse Principle)

REP: 재사용/릴리스 등가 원칙#

재사용 단위는 릴리스 단위와 같습니다.

  • 소프트웨어 컴포넌트가 릴리스 절차를 통해 추적 관리되지 않거나 릴리스 번호가 부여되지 않는다면 해당 컴포넌트를 재사용할 수 없습니다.
  • 릴리스 번호가 없다면 재사용 컴포넌트들이 서로 호환되는 지 보증할 방법이 전혀 없습니다.
  • 소프트웨어 설계와 아키텍처 관점에서 보면 단일 컴포넌트는 응집성 높은 클래스와 모듈들로 구성되어야 함을 의미합니다.
    • 컴포넌트를 구성하는 모든 모듈은 서로 공유하는 중요한 테마나 목적이 있어야 합니다.
  • 하나의 컴포넌트로 묶인 클래스와 모듈은 반드시 함께 릴리스할 수 있어야 합니다.
  • 이 조건으로만 클래스와 모듈을 묶는 방법을 설명하기 힘들기에 조건이 약합니다.
    • 즉, 이 원칙을 어기면 '이치에 맞지' 않게 됩니다.
  • 이 원칙의 약점은 다음에 다룰 두 원칙이 지닌 강점을 통해 충분히 보완할 수 있습니다.
    • CCP와 CRP는 REP를 엄격하게, 제약을 가하는 측면에서 정의합니다

CCP: 공통 폐쇄 원칙#

동일한 이유로 동일한 시점에 변경되는 클래스를 같은 컴포넌트로 묶습니다. 서로 다른 시점에 다른 이유로 변경되는 클래스는 다른 컴포넌트로 분리합니다.

  • 이 원칙은 단일 책임 원칙(SRP)을 컴포넌트 관점에서 다시 쓴것입니다.
    • SRP에서 단일 클래스(class) 는 변경의 이유가 여러 개 있어서는 안 된다고 말하듯이, 공통 폐쇄 원칙(CCP) 에서도 마찬가지로 단일 컴포넌트(component) 는 변경의 이유가 여러 개 있어서는 안 된다고 말합니다.
  • 대다수의 애플리케이션에서 유지보수성(maintainability) 은 재사용성보다 훨씬 중요합니다.
    • 애플리케이션에서 코드가 반드시 변경되어야 한다면, 이러한 변경이 여러 컴포넌트 도처에 분산되어 발생하기보다는 차라리 변경 모두가 단일 컴포넌트에서 발생하는 편이 낫습니다.
    • 변경을 단일 컴포넌트로 제한할 수 있다면, 해당 컴포넌트만 재배포하면 됩니다.
  • CCP는 같은 이유로 변경될 가능성이 있는 클래스는 모두 한곳으로 묶을 것을 권합니다.
    • 물리적 또는 개념적으로 강하게 결합되어 항상 함께 변경되는 클래스들은 하나의 컴포넌트에 속해야합니다.
    • 이를 통해 소프트웨어를 릴리스, 재검증, 배포하는 일과 관련된 작업량을 최소화할 수 있습니다.
  • 발생할 가능성이 있거나 과거에 발생했던 대다수의 공통적인 변경에 대해서 클래스가 닫혀 있도록 설계합니다.
  • CCP에서는 동일한 유형의 변경에 대해 닫혀 있는 클래스들을 하나의 컴포넌트로 묶음으로서, 변경이 필요한 요구사항이 발상할 때 그 변경이 영향을 주는 컴포넌트들이 최소한으로 한정될 가능성이 확실히 높아집니다.

SRP와의 유사성#

  • CCP는 컴포넌트 수준의 SRP입니다.

동일한 시점에 동일한 이유로 변경되는 것들을 한데 묶어라. 서로 다른 시점에 다른 이유로 변경되는 것들은 서로 분리합니다.

CRP: 공통 재사용 원칙#

컴포넌트 사용자들을 필요하지 않는 것에 의존하게 강요하지 않습니다.

  • 공통 재사용 원칙(CRP)도 클래스와 모듈을 어느 컴포넌트에 위치시킬지 결정할 때 도움되는 원칙입니다.
    • CRP에서는 같이 재사용되는 경향이 있는 클래스와 모듈들은 같은 컴포넌트에 포함해야 한다고 말합니다.
  • 개별 클래스가 단독으로 재사용되는 경우는 거의 없습니다.
    • 일반적으로는 재사용 가능한 클래스는 재사용 모듈의 일부로써 해당 모듈의 다른 클래스와 사용작용하는 경우가 많습니다.
    • 컴포넌트 내부에서는 클래스들 사이에 수많은 의존성이 있으리라고 예상할 수 있습니다.
  • 간단한 예시로 컨테이너(container) 클래스와 해당 클래스의 이터레이터(iterator) 클래스를 들 수 있으며 강결합되어 있기에 함께 재사용됩니다. 즉, 이 클래스들은 반드시 동일한 컴포넌트에 위치해야 합니다.
  • CRP는 동일한 컴포넌트로 묶어서는 안되는 클래스가 무엇인지도 알려줍니다.
    • 컴포넌트가 다른 컴포넌트를 사용하면 두 컴포넌트 사이는 의존성이 생깁니다.
    • 이러한 의존성이 있는 경우, 사용되는 컴포넌트가 변경될 때마다 사용하는 컴포넌트를 변경하거나 재컴파일, 재검증, 재배포 가능성이 남아있습니다.
  • 의존하는 컴포넌트가 있다면 핻아 컴포넌트의 모든 클래스에 대해 의존함을 확실히 인지해야합니다.
  • CRP는 어떤 클래스를 한데 묶어도 되는지보다는, 어떤 클래스를 묶어서 안되는지에 대해서 훨씬 더 많은 것을 이야기합니다.
    • CRP는 강하게 결합되지 않은 클래스들을 동일한 컴포넌트에 위치시켜서는 안 된다고 이야기합니다.

ISP와의 관계#

CRP는 인터페이스 분리 원칙(ISP)의 포괄적인 버전입니다.

  • ISP는 사용하지 않는 메서드가 있는 클래스에 의존하지 말라고 조언하며, CRP는 사용하지 않는 클래스를 가진 컴포넌트에 의존하지 말라고 조언합니다.

필요하지 않은 것에 의존하지 않습니다.

컴포넌트 응집도에 대한 균형 다이어그램#

  • 응집도에 관한 세 원칙은 상충됩니다.
  • REP와 CCP는 포함(inclusive) 원칙이며, 컴포넌트를 더욱 크게 만듭니다.
  • CRP는 배제(exclusive) 원칙이며, 컴포넌트를 더욱 작게 만듭니다.

결합도 원칙들의 균형 다이어그램

  • REP와 CRP에만 중점을 두면 사소한 변경이 생겼을 때 너무 많은 컴포넌트에 영향을 미치며 반대로 CCP나 REP에만 과도하게 집중되면 불필요한 릴리스가 너무 빈번해집니다.
  • 뛰어난 아키텍트라면 이 균형 삼각형에서 개발팀이 현재 관심을 기울이는 부분을 충족시키는 위치를 찾아야하며, 시간이 흐르면서 우선순위가 바뀌는 부분도 인지해야합니다.
    • 초기에는 CCP가 REP보다 훨씬 중요합니다. (개발가능성이 재사용성보다 중요하므로)
  • 일반적으로 프로젝트는 삼각형의 오른쪽에서 시작하는 편이며, 오직 재사용성만 희생하면 됩니다.
    • 프로젝트가 성숙해지고 파생된 또 다른 프로젝트가 시작되면서 프로젝트는 삼각형에서 점차 왼쪽으로 이동합니다.
    • 프로젝트의 컴포넌트 구조는 시간과 성숙도에 따라 변합니다.

결론#

  • 클래스들을 묶어서 컴포넌트로 만들지를 결정할 때, 재사용성과 개발 가능성이라는 상충하는 힘을 반드시 고려해야 합니다.
  • 이들 사이에서 애플리케이션의 요구에 맞게 균형을 잡는 일은 중요합니다.

시간에 흐름에 따라 프로젝트의 초점이 개발가능성에서 재사용성으로 바뀌고, 그에 따라 컴포넌트를 구성하는 방식도 조금씩 흐트러지고 또 진화합니다.


14장. 컴포넌트 결합#

아래의 세가지 원칙은 컴포넌트 사이의 관계를 설명합니다. 개발 가능성과 논리적 설계 사이의 균형을 다룹니다.

ADP: 의존성 비순환 원칙#

컴포넌트 의존성 그래프에 순환(cycle)이 있어서는 안됩니다.

  • 누군가가 마지막으로 수정한 코드 때문에 다른 사람들의 작업이 안되는 경우가 큰 프로젝트에서는 비일비재합니다.
  • 이 문제의 큰 해결책은 '주 단위 빌드(weekly build)''의존성 비순환 원칙(Acyclic Dependencies Principle, ADP)' 입니다.

주 단위 빌드(Weekly Build)#

  • 주 단위 빌드는 중간 규모의 프로젝트에서는 흔하게 사용됩니다.
    • 첫 4일 동안은 작업을 하고 금요일이 되면 변경된
  • 5일 중 4일 동안 개발자를 고립된 세계에서 살 수 있게 보장해 주는 장점을 가집니다.
  • 프로젝트가 커지면 프로젝트 통합은 하루만에 하기 어려워집니다.
  • 개발보다 통합에 드는 시간이 늘어나게 되면 팀의 효율성도 나빠지게 됩니다.
    • 즉, 효율성을 위해 빌드 일정을 계속 늘려야 하고, 빌드 주기가 늦어질수록 통합과 테스트를 수행하기 어려워지고 팀은 빠른 피드백을 못하게 됩니다.

순환 의존성 제거하기#

  • 문제의 해결책은 개발 환경을 릴리스 가능한 컴포넌트 단위로 분리하는 것입니다.
    • 이를 통해 컴포넌트는 개별 개발자 또는 단일 개발팀이 책임질 수 있는 작업 단위가 됩니다.
    • 개발자가 해당 컴포넌트가 동작하도록 만든 후, 해당 컴포넌트를 릴리스하여 다른 개발자가 사용할 수 있도록 만듭니다.
    • 개발자는 자신만의 공간에서 해당 컴포넌트를 지속적으로 수정하고, 나머지 개발자는 릴리스된 버전을 사용합니다.
  • 컴포넌트가 새로 릴리스되어 사용할 수 있게 되면, 다른 팀에서는 새 릴리스를 당장 적용할지를 결정합니다.
    • 적용하지 않기로 했다면 그냥 과거 버전의 릴리스를 계속 사용하고, 준비가 됬다면 새 릴리스를 사용합니다.
  • 작업 절차는 단순하며 합리적이라 널리 사용되는 방식입니다. 다만 의존성 구조에 순환이 있어서는 안됩니다.
  • 컴포넌트 다이어그램에서는 컴포넌트를 조립하여 애플리케이션을 만드는 다소 전형적인 구조를 볼 수 있습니다.
    • 중요한 점은 컴포넌트 간의 의존성 구조입니다. 이 구조는 방향 그래프(directed graph) 입니다.
    • 컴포넌트는 정점(vertex)에 해당하고, 의존성 관계는 방향이 있는 간선(Directed Acyclic Graph, DAG) 에 해당합니다.
  • 더 주목할 점은 어느 컴포넌트에서 시작하더라도, 의존성 관계를 까라가면서 최초의 컴포넌트로 되돌아갈 수 없습니다. 즉, 이 구조를 비순형 방향 그래프(Directed Acyclic Graph, DAG) 라고 합니다.

전형적인 컴포넌트 다이어그램

  • 위 예시에서 Main은 새로 릴리스되더라도 시스템에서 이로 인해 영향받는 컴포넌트가 전혀 없습니다.
    • Presenters를 테스트할때는 InteractorsEntites 를 이용해서 Presenters 자체 버전을 빌드하면 됩니다.
  • 시스템 전체를 릴리스해야 할 때가 오면 릴리스 절차는 상향식으로 진행합니다.

순환이 컴포넌트 의존성 그래프에 미치는 영향#

  • 순환 참조가 있는 경우, 해당 컴포넌트의 개발자들은 모두 서로에게 얽매이기 때문에 모두 항상 정확하게 동일한 릴리스를 사용해야 하는 문제가 생깁니다.
  • 순환이 생기면 컴포넌트를 분리하기도 어려워집니다.
    • 릴리스시 에러가 상당히 많아지며, 모듈의 개수가 많아짐에 따라 빌드 관련 이슈는 기하급수적으로 증가합니다.
  • 의존성 그래프에 순환이 생기면 컴포넌트를 어던 순서로 빌드해야 올바를지 파악하기가 상당히 힘들어집니다.

순환 끊기#

컴포넌트 사이의 순환을 끊고 의존성을 다시 DAG로 원상복구하는 일은 언제라도 가능합니다. 주요 메터니즘은 두가지 입니다.

  1. 의존성 역전 원칙(DIP)를 적용합니다.
  2. 모두 의존하는 새로운 컴포넌트를 만듭니다.

흐트러짐(Jitters)#

  • 두번째 해결책을 보면 요구사항이 변경되면 컴포넌트 구조도 변경될 수 있다는 사실입니자.
    • 즉, 애플리케이션이 성장함에 따라 컴포넌트 의존성 구조는 서서히 흐트러지며 성장합니다.
    • 따라서 의존성 구조에 순환이 발생하는지 항상 관찰해야합니다.

하향식(top-down) 설계#

  • 위 내용의 결론은 컴포넌트 구조는 하향식으로 설계될 수 없습니다.
    • 컴포넌트는 시스템에서 가장 먼저 설계할 수 있는 대상이 아니며, 오히려 시스템이 성장하고 변경될 때 함께 진화합니다.
  • 컴포넌트 의존성 다이어그램은 애플리케이션의 빌드 가능성(buildability)유지보수성(maintainability) 을 보여주는 지도(map) 와 같습니다.
    • 이러한 이유는 컴포넌트 구조는 초기에 설계할 수 없습니다.
    • 빌드하거나 유지보수할 소프트웨어가 없다면 빌드와 유지보수에 관한지도 또한 필요없습니다.
  • 프로젝트가 커지면서 단일 책임 원칙(SRP)와 공통 폐쇄 원칙(CCP)에 관심을 갖기 시작하고, 이를 적용해 함께 변경되는 클래스는 같은 위치에 배치되도록 만듭니다.
  • 의존성 구조와 관련된 최우선 관심사는 변동성을 격리하는 일입니다.
    • 컴포넌트 의존성 그래프는 자주 변경되는 컴포넌트로부터 안정적이며 가치가 높은 컴포넌트를 보호하려는 아키텍트가 만들고 가다듬게 됩니다.
  • 클래스도 설계하지 않은 상태에서 컴포넌트 의존성 구조를 설계하려고 시도한다면 실패할 가능성이 높습니다.
    • 재사용 가능성 요소도 알지 못하며, 컴포넌트를 생성할 때 거의 확실한 순환 의존성이 발생할 것입니다.
    • 따라서 컴포넌트 의존성 구조는 시스템의 논리적 설계에 발맞춰 성장하며 또 진화합니다.

SDP: 안정된 의존성 원칙#

안정된 방향으로 의존하라.

  • 설계는 정적일 수 없으며, 설계를 유지하다 보면 변경은 불가피합니다.
    • 공통 폐쇄 원칙을 준수함으로써, 컴포넌트가 다른 유형의 변경에는 영향받지 않으면서 특정 유형의 변경에만 민감하게 만들 수 있습니다.
    • 컴포넌트 중 일부는 변동성을 지니도록 설계되며, 언젠간 변경됨을 예상합니다.
  • 변경이 쉽지 않은 컴포넌트가 변동이 예상되는 컴포넌트에 의존하게 만들어서는 절대로 안됩니다.
    • 한번 의존하게 되면 변동성이 큰 컴포넌트도 결국 변경이 어려워집니다.
  • 안전된 의존성 원칙(Stable Dependencies Principle, SDP)을 준수하면 변경하기 어려운 모듈이 변경하기 쉽게 만들어진 모듈에 의존하지 않도록 만들 수 있습니다.

안전성#

  • 소프트웨어 컴포넌트를 변경하기 어렵게 만드는 확실한 방법 하나는 수많은 다른 컴포넌트가 해당 컴포넌트에 의존하게 만드는 것입니다.

X는 안정된 컴포넌트

  • 세 컴포넌트가 X에 의존합니다. X는 세 컴포넌트를 책임진다(responsible) 이라고 말합니다. 반대로 X는 어디에도 의존하지 않으므로 이 경우 X는 독립적이다(independent) 라고 말합니다.

Y는 상당히 불안정한 컴포넌트

  • 위 경우에는 Y는 책임성이 없고, 의존적이라고 말할 수 있습니다.

안정성 지표#

  • 컴포넌트로 들어오고 나가는 의존성의 개수를 세어 보는 방법이 있을 수 있습니다. 이 숫자를 통해 컴포넌트가 위치상의 개수를 세어 보는 방법이 있을 수 있습니다.
    • Fan-in : 안으로 들어오는 의존성,, 내부의 클래스에 의존하는 컴포넌트 외부의 클래스 개수
    • Fan-out : 밖으로 나가는 의존성, 의부 클래스에 의존하는 컴포넌트 내부의 클래스 개수
    • I(불안전성) : I = Fan-out % (Fan-in + Fan-out), 0과 1사이의 값을 가집니다.
      • 0인 경우 최고의 안정된 컴포넌트, 1인 경우 최고로 불안정한 컴포넌트라는 의미입니다.
  • Fan-inFan-out 지표는 특정 컴포넌트 내부의 클래스에 의존하는, 컴포넌트 외부에 위치한 클래스의 개수를 세어서 계산할 수 있습니다.
  • 컴포넌트의 I 지표는 그 컴포넌트가 의존하는 다른 컴포넌트의 I보다 커야합니다.

모든 컴포넌트가 안정적이어야 하는 것은 아니다#

SDP 위배

  • Flexible은 변경하기 쉽도록 설계했지만, 의존성이 걸리므로 변경이 어려워집니다.

추상 컴포넌트

  • 추상 컴포넌트는 상당히 안정적이며, 덜 안정적인 컴포넌트가 의존할 수 있는 이상적인 대상입니다.
  • 루비나 파이썬 같은 동적 타입 언어를 사용할 때는 이러한 추상 컴포넌트가 전혀 존재하지 않을 뿐만 아니라, 추상 컴포넌트로 향하는 의존성은 없습니다.

SAP: 안정된 추상화 원칙#

컴포넌트는 안정된 정도만큼만 추상화되어야 합니다.

고수준 정책을 어디에 위치시켜야 하는가?#

  • 시스템에는 자주 변경해서는 절대 안되는 소프트웨어도 존재합니다.
    • ex) 고수준 아키텍처나 정책 결정과 관련된 소프트웨어
    • 불안정한 컴포넌트(I=1)는 반드시 변동성이 큰 소프트웨어, 즉 쉽고 빠르게 변경할 수 있는 소프트웨어만을 포함해야 합니다.
  • 고수준 정책을 안정된 컴포넌트에 위치시키면, 그 정책을 포함하는 소스 코드는 수정하기가 어려워집니다.
    • 이로 인해 시스템 전체 아키텍처가 유연성을 잀습니다.
  • 컴포넌트가 최고로 안정된 상태이면서도(I=0) 동시에 변경에 충분히 대응할 수 있을 정도로 유연하게 만들 수 있는 방법이 필요합니다.
    • 이를 개방 폐쇄 원칙(OCP) 에서 찾을 수 있습니다.
    • 추상 클래스가 이러한 원칙을 준수합니다.

안정된 추상화 원칙#

  • 안정된 추상화 원칙(Stable Abstraction Principle, SAP)는 안정성(Stability)와 추상화 정도(abstractness) 사이의 관계를 정의합니다.
  • 안정적인 컴포넌트라면 반드시 인터페이스와 추상 클래스로 구성되어 쉽게 확장할 수 있어야 합니다.
    • 안정한 컴포넌트가 확장이 가능해지면 유연성을 얻게 되고 아키텍처를 과도하게 제약하지 않게 됩니다.
  • SAP와 SDP를 결합하면 컴포넌트에 대한 DIP나 마찬가지가 됩니다.
    • 의존성은 추상화의 방향으로 향하게 됩니다.
  • DIP는 클래스에 대한 원칙이며, 클래스는 추상적이거나 아니거나입니다.
    • SDP와 SAP의 조합은 컴포넌트에 대한 원칙이며, 컴포넌트는 어떤 부분은 추상적이면서 다른 부분은 안정적일 수도 있습니다.

추상화 정도 측정하기#

  • A 지표는 컴포넌트의 추상화 정도를 측정한 값입니다. 이 값은 컴포넌트의 클래스 총 수 대비 인터페이스와 추상 클래스의 개수를 단순히 계산한 값입니다.
    • Nc: 컴포넌트의 클래스 개수
    • Na: 컴포넌트의 추상 클래스와 인터페이스의 개수
    • A: 추상화 정도, A = Na % Nc
  • A 지표는 0과 1 사이의 값을 가지며 0이면 컴포넌트에 추상 클래스가 없다는 뜻이고 A가 1이면 오로지 추상 클래스만을 포함합니다.

주계열#

  • 안정성(I)와 추상화 정도(A) 사이의 관계를 정의해야 합니다.

배제 구역, Zone of Exclusion

고통의 구역#
  • 제대로 설계된 컴포넌트라면 근체에는 위치하지 않을 거라고 보는 게 일반적입니다.
  • (0, 0) 구역은 배제해야할 구역이며, 고통의 구역이라고 부릅니다.
  • 종종 소프트웨어 엔티티는 고통의 구역에 위치합니다. 특히 데이터베이스 스키마는 변동성이 높기로 유명하며 구체적이며, 많은 컴포넌트가 여기에 의존하였습니다.
  • 변동성이 없는 컴포넌트는 (0, 0) 구역에 위치했더라도 해롭지 않습니다.

즉, 고통의 구역에서 문제가 되는 경우는 변동성이 있는 소프트웨어 컴포넌트입니다.

쓸모없는 구역#
  • (1, 1) 구역은 추상적이지만, 누구도 그 컴포넌트에 의존하지 않기 때문입니다. 따라서 이 영역은 쓸모없는 구역이라고 부릅니다.
  • 이 코드는 누구도 구현하지 않은채 남겨진 추상 클래스인 경우가 많습니다.
  • 쓸대없는 구역 내부 깊숙이 자리 잡은 컴포넌트은 이러한 엔티티의 상당부분을 포함할 가능성이 높습니다.
배제 구역 벗어나기#
  • 변동성이 큰 컴포넌트 대부분은 두 배제 구역으로부터 가능한 멀리 떨어트려야 합니다.
  • 주 계역에 위치하는 컴포넌트는 자신의 안전성에 비해 너무 추상적이지 않고, 추상화 정도에 비해 너무 불안정하지도 않습니다.
  • 컴포넌트가 위치할 수 있는 가장 바람직한 지점은 주계역의 두 종점입니다.
    • 뛰어난 아키텍트라면 대다수의 컴포넌트가 두 종점에 위치하도록 만듭니다.
  • 컴포넌트는 주계열 바로 위 또는 가갑게 위치할 때 가장 이상적입니다.
주계열과의 거리#
  • 위에서 세 번째 지표가 도출됩니다.
    • D: 거리, D = |A + I - 1|
    • D가 0이면 컴포넌트가 주계역 바로 위에 위치한다는 뜻이며, 1이면 주계열로부터 가장 멀리 위치한다는 뜻입니다.
  • D에 가깝지 않는 컴포넌트가 있다면 해당 컴포넌트는 재검토한 후 재 구성할 수 있습니다.
  • 극히 예외적인 컴포넌트를 식별할 수 있습니다.

컴포넌트 산점도

결론#

  • 의존성 관리 지표는 설게의 의존성과 추상화 정도가 '훌륭한' 패턴이라고 생각하는 수준에 얼마나 잘 부합하는지를 측정합니다.
  • 좋은 의존성도 있으며 나쁜 의존성도 있습니다.
  • 지표는 일정의 평가 지표이며 이또한 불완전한 정보입니다.
Last updated on