'High Cohesion'에 해당되는 글 1건

  1. 2010.10.04 OOD - 커플링이란 무엇이며, 어떻게 줄일 수 있을까? (2)

커플링 (Coupling) 은 응집도(Cohesion)과 함께 소프트웨어 설계의 품질을 판단하는 척도 입니다. 매우 중요한 개념이지만 정확하게 설명하는 글을 찾기가 쉽지 않습니다. 

모든 설계에 관한 글에서 커플링을 느슨하게 하라고 말합니다. 느슨한 커플링 이더라도 너무 많은 커플링은 전체를 타이트 하게 만듭니다. 

 커플링이란 완벽하게 제거되야할 악의 축인 것일 까요?

아래의 포스트는 IEEE SOFTWARE 에 2001년   파울러가 기고한 Reducing Coupling 글의 번역한 것 입니다.

커플링이란 무엇이며, 커플링을 줄이는 방법은 무엇인지 마틴 파울러 로 부터 배우 봅니다.

요런게 커플링? (couple rings ?, coupling !)




번역 :

Reducing Coupling

by 마틴 파울러.
IEEE SOFTWARE J u l y / A u g u s t 2 0 0 1 , Reducing Coupling , by Martin Fowler


커플링(Coupling)은 응집력(Cohesion)과 함께 설계의 품질을 결정하는 가장 오래된 지표 중 하나 입니다. 구조적 설계 때 부터 그 유산은 사라지지 않았습니다. 저는 소프트웨어를 설계 할때 항상 염두에 둡니다. 커플링을 설명하는 방법이 여러개가 있으나 다음으로 요약됩니다: 어떤 모듈을 변경할때 다른 모듈의 변경이 요구된다면 커플링이 존재합니다. 한 측면에서 두개의 모듈이 비슷한 일을 한다면 다른 코드에 중복되기 쉽습니다. 이는 명확하게 중복의 예 입니다.  중복은 언제나 커플링을 수반합니다. 두개의 코드 조각이 명확한 관계를 맺고 있지 않기 때문에 중복 지점을 집어내기란 쉽지 않습니다.

커플링은 또 어떤 모듈이 다른 모듈의 코드를 사용할 때 발생합니다. 아마도 데이터를 접근하거나 함수를 호출할 때 말이죠. 이런 경우에는 코드 중복과는 다르게 명확하게 집어 낼 수 있습니다. 커플링이란 완전히 제거 해야 하는 요소로 대할 수 없습니다. 여러분은 하나의 프로그램을 여러개의 모듈로 나눌 수 있지만 모듈들은 어떠한 방법으로든 커뮤니케이트가 필요합니다. - 다른 측면으로, 다수의 프로그램을 가지고 있을 때, 커플링은 바람직 합니다. 커플링을 완전히 제거하려 한다면 모든 것을 하나에 집어 넣은 거대한 모듈만이 남게 될 것입니다. 그리고 거기에는 매우 많은 커플링이 존재하게 됩니다. - 단지 러그(rug)로 덮어서 감추어 둔 것일 뿐이죠.

커플링은 우리가 통제해야 할 대상인 것입니다. 그러면 어떻게 통제 할 까요? 모든 곳에서 걱정해야 할 대상일까요? 아니면 더 중요한 특정 위치가 있는 걸 까요? 어떤 요소가 나쁜 커플링이라고 생각되며, 용인되는 커플링이란 어떤 것일 까요?

의존에서 살펴보기

높은 레벨의 모듈에서의 커플링에 대해 생각해 보았습니다. 우리가 시스템을 12개 정도의 큰 모듈 조각으로 나누었을때 이것들을 어떻게 묶을 까요?  저는 coarser-gained 모듈에 집중하였습니다. 모든 커플링을 고려하는 것은 불가항력이기 때문이죠. 가장 큰 문제는 상위(upper level)에서의 커플링을 통제할 수 없을 때 라는 것입니다. 저는 여러개의 모듈이 서로 묶여 있다 하더라도 걱정하지 않습니다, 하지만 모듈간의 의존(dependency) 관계의 패턴을 찾습니다. 이때 다이어그램을 이용하는 것이 매우 유용합니다.

제가 의존(dependency) 이라는 용어를 사용할 때는 UML에서 정의 된 것을 의미합니다. 고로 UI 모듈이 도메인 모듈의 어떤 코드를 참조 한다면, UI 모듈이 도메인 모듈에 의존한다고 말 할 수 있습니다.- 함수 호출, 데이터 이용, 도메인 모듈에 정의된 타입을 이용하는 것 말이죠.
어떤 사람이 도메인 모듈을 변경한다면 UI 모델의 변경이 요구됩니다. 의존(dependency)은 단방향성 입니다. : UI 모듈은 항상 도메인 모듈을 의존합니다. 다른 방법은 존재하지 않습니다. 도메인 모듈 또한 UI 모듈을 의존 한다면 우리는 두번째 의존을 가지는 것입니다.

UML 의존은 전이 불가(non transitive)를 의미합니다. UI 모듈이 도메인 모듈을 의존하고, 도메인모듈이 데이터베이스 모듈을 의존 한다면, 우리는 UI 모듈이 데이터베이스 모듈을 의존한 다고 가정할 수 없습니다. 그렇게 가정한다면 우리는 UI 모듈과 데이터베이스 모델을 직접적인 부가 의존을 기술해야 합니다. 이 비전이성(nontransivity)은 도메인모델이 데이터베이스의 변경으로 부터 UI를 격리시킨다는 것을 보여주기 때문에 매우 중요합니다. UI 는 오로지 도메인 모델의 인터페이스가 변경될 만큼 매우 많은 데이터베이스의 변경이 발생 했을 때에만 변경됩니다. 그림 1a 는 UML 표기를 이용한 다이어그램을 보여 줍니다. UML은 객체지향 시스템을 위해 설계 되었지만, 모듈의 기본 개념과 의존은 대부분의 소프트웨어에 적용됩니다.

이런 종류의 고-레벨(high-level) 모듈의 UML 이름은 패키지(Pakage) 입니다. 그래서 이후 부터는 이 용어를 사용하겠습니다. 패키지들 이기 때문에 저는 이런 종류의 다이어그램을 패키지 다이어그램(pakage diagram) 이라고 부르겠습니다. (UML 로 엄격하게 말해서는 클래스 다이어그램 입니다.)

여기에서 기술하려 하는 것은 레이어드 아키텍처(layered architecture) 입니다. 정보 시스템(information system)에서 종사하는 사람은 누구나 익숙 할 겁니다. 정보시스템에서  레이어는 우리가 의존을 생각할 때 반드시 고려해야 하는 어떤 것을 기술하는 좋은 재료입니다.

의존구조를 심사숙고하는 충고의 가장 공통적인 것은 순환을 회피 하라는 것 입니다. 순환은 여러분의 모든 변경이 다시 최초 패키지의 변경을 요구하기 때문에 매우 큰 문제입니다. 그런 시스템은 수차례에 걸쳐 순환하며 살펴 봐야 하기 때문에 이해하기가 매우 어렵습니다.  저는 엄격한 규칙으로서 패키지들 사이의 순환 회피를 보여주여줄 필요를 느끼지 않습니다.-그것들이 지역화 되어 있다면 저는 관대할 겁니다.




매퍼 패키지(A mapper pakage)

그림 1a, 모든 의존은 한 방향으로 작동합니다. 이는 잘 통제되고 있는 의존 집합임을 나타냅니다.(필수 요건은 아닙니다만). 그림 1b,는 매퍼 패키지가 데이터베이스로 부터 도메인을 분리시켜주는 정보시스템의 또다른 일반적인 요소를 보여 줍니다(매퍼 패키지는 양방향 격리를 제공합니다.), 이것은 도메인과 데이터베이스의 변경이 서로 독립적이 될 수 있도록 합니다. 그 결과로 여러분은 더욱 복잡한 객체지향 모델에서 이런 스타일을 자주 발견할 수 있습니다.

물론, 여러분이 데이터를 불러오거나 저장할때 이 그림이 정확하게 옳지는 않단느 것을 알게 될 겁니다.  도메인의 모듈이 데이터베이스로 부터 데이터가 필요할 때 어떻게 요청하지? 매퍼에게 물어 볼 수 없습니다. 그게 가능하다면 도메인에서 매퍼로의 의존이 나타나게 되고, 순환 의존이 됩니다. 이 문제를 풀기 위해서 우리는 다른 종류의 의존이 필요합니다.

그래서, 저는 다른 부분의 코드를 사용하는 용어로서의 의존을 말해 왔습니다. 하지만, 다른 종류의 관계가 존재 합니다.- 인터페이스(Interface) 와 구현 (implementation)과의 관계 처럼 말이죠. 구현은 인터페이스를 의존 하지만 역(반대)은 아닙니다. 오로지 인터페이스 만을 의존하는 인터페이스를 호출하는 특정 상황에서도, 심지어는 분리된 모듈이 그것을 구현한다고 해도 말이죠.



그림 2는 이 아이디어를 묘사합니다. 도메인은 구현이 아니라 인터페이스를 의존합니다. 도메인은 몇몇 매퍼 구현 없이는 일을 할 수 없습니다. 하지만, 오로지 인터페이스의 변경만이 도메인의 변경을 유발 할 수 있습니다.

이런 상황에서 분리된 패키지가 존재하지만 꼭 필요하지는 않습니다.



그림3은 도메인에 포함된 저장 패키지(store pakage)를 보여줍니다. 매퍼까지 포함된 저장 구현을 구현합니다. 이 경우에, 도메인은 매퍼의 인터페이스를 정의 합니다. 도메인 패키지는 저장 인터페이스를 구현한느 어떤 매퍼라도 선택해서 함께 일 할 것이라는 것을 말해 줍니다.


모듈에 분리된 모듈의 구현을 의도하는 인터페이스를 정의하는 것은 의존을 깨고 커플링을 감소시키는 기반 방법입니다.  이런 접근 방법은 다양한 형태로 나타납니다. 가장 기본적인 형태가 call back 입니다. 이 형태에서 호출자는 특정 시그니처를 포함한 함수의 참조를 요구합니다. 자바 세상에서 일반적인 예는 리스너 입니다. 리스너는 어떤 것을 명확화하는 보다 명시적인 클래스 입니다.

다른 예는 모듈에 전달(passes out) 될 수 있는 이벤트(event)를 정의하는 것입니다. 여러분은 모듈의 확인을 듣고(linstening)있는 인터페이스를 정의하는 이벤트를 생각해 볼 수 있습니다. call back 함수의 호출자는 리스너 인터페이스를 정의하고 있는 녀석입니다. 그리고 이벤트의 생성자는 모듈의 호출 되는 명확한 장소를 모릅니다. 결과적으로 여기에는 의존이 없습니다.


저는 뭔가 마무리 하지 못한  느낌이 듭니다. "잘-통제된 의존" 과 같은 단어를 사용하여 모호하게 만들었기 때문이죠. 잘 통제된 의존 집합 과 같은 것을 정의 하려 시도 할때 견고한 가이드를 제공하는 것은 매우 어렵습니다. 명확하게도 이글은 의존의 양을 줄이는 것에 대한 것입니다. 하지만 그것이 전체의 이슈는 아닙니다. 의존의 방향(direction) 과 흐르는(flow) 방법, 큰 순환을 제거하는 것들 또한 중요합니다. 덧붙여서 , 저는 모든 의존을 동일하게 취급합니다. 인터페이스의 넖이를 제외하고는 말이죠.

거기에 의존이 있다는 사실 보다 그 의존이 무엇이냐를 지나치게 걱정하는 것처럼 보일 수 있습니다.


제가 따르는 기본 규칙은 고-레벨 의존들을 시각화하고 합리화 한 다음, 내가 원치 않는 의존을 깨기 위해 구현으로 부터 인터페이스를 분리 하는 것입니다. 설계에 대한 많은 경험적인 판단들 처럼, 이것 또한 끔찍하게 불완전 하게 보입니다. 하지만, 저는 이것이 유용하다는 것을 알게 됐고, 결국에는 상황에 달려 있습니다.

### 끝.


IEEE SOFTWARE J u l y / A u g u s t 2 0 0 1 , Reducing Coupling , by Martin Fowler

신고

이 글을 Twitter / Facebook 에 공유하기
이 글이 유익하다면 아래의 트위터 버튼을 눌러 공유해 주시거나, 페이스북 "좋아요" 버튼을 눌러 주세요.

   


Posted by 반더빌트


티스토리 툴바