본문 바로가기

issue & tip

결합도

반응형

결합도

- 모듈간에 상호 의존하는 정도 또는 두 모듈 사이의 연관 관계를 의미


목표

- 모듈 상호간 낮은 결합도 추구하여 의존하는 모듈이 적어야한다.

- 오류 발생시 전파되어 다른 오류의 원인이 되는 파급 효과를 최소화해야한다.


모듈 사이의 결합은 강한 정도(가장 바람직하지 못한 경우)에서부터 약한 정도(가장 바람직한 경우)에 이르기까지 나열할 수 있다. 다음 보기 중 강한 정도에서 약한 정도 순으로 가장 적절하게 나열된 것은?

 

1. 내용결합도 - 공통결합도 - 제어결합도 - 스템프 결합도 - 자료결합도


소프트웨어의 모듈간의 결합도(coupling)와 모듈내 요소간의 응집도(cohesion)에 대해서 소프트웨어 설계시 가장 바람직 한 것은?

 

1. 응집도는 높게 결합도는 낮게 설계한다.



 다음중에서 소프트웨어 설계시 결합도와 응집도 측면에서 가장 바람직한 항목으로 묶여진 것은 어느것인가?

3. 자료 결합(data coupling) – 기능적 응집(functional cohesion)



- 모듈끼리 갖는 관계의 밀접함을 나타내는 척도로, 어떤 결합의 굵기를 측정하는 단위

- 6단계로 단계가 높을수록(숫자가 클수록) 관계가 약하고 느슨한 결합이며 좋은 모듈이라고 함

- 모듈 간에 데이터를 얼마나 교환하는지에 주목해서 판정


1.내용결합(content coupling)

한 모듈과 다른 모듈이 일부를 공유하는 모듈 결합방식

다른 모듈 내의 외부에 선언되지 않은 데이터를 직접 참조하거나 명령의 일부를 공유하는 경우

내용 결합은 고수준 언어를 사용하는 모듈에서는 발견되지 않지만, 어셈블러 언어 등을 사용하는 모듈에서는 종종 발견된다.

내용 결합은 한쪽 모듈의 변경이 다른 모듈에 영향을 미친다.

다른 모듈의 상황을 고려해 가면서 변경하기란 힘든 작업이므로, 내용결합에서는 오류가 직결될 확률이 높다.


- 한 모듈이 다른 모듈의 내부 기능 및 자료를 직접 참조하거나 수정하는 경우

- 한 모듈에서 다른 모듈의 중간으로 분기되는 경우에도 내용 결합도에 해당된다.


하나의 모듈이 다른 모듈의 내부 동작을 수정하거나 내부 동작에 의존

캡슐화에 의한 은닉화가 필요하다.


module A {

main() {

B.setData(123);

}

}


moudule B {

data  = 1;

setData(data);

}


2.공통결합

공통영역에 정의된 데이터를 몇 개 모듈이 공동으로 사용하는 모듈 결합방식이다.

공통영역에 정의된 데이터란 이른바 전역변수를 말한다.

공통 결합은 결합도가 높고 단점이 많다.

- 공통 영역 데이터는 모듈 간의 인터페이스 상에 나타나지 않으므로 코드 해독이 매우 어렵게 만든다.

- 공통영역 데이터는 원래 해당 데이터와 관계가 없는 모듈에서도 마음만 먹으면 사용할 수 있으므로 코드의 안전성이 낮아진다.

- 공통 결합된 모듈은 공통 영역 데이터를 통해 여러가지 모듈과 이어져 있으므로 재사용성이 저해된다.


모듈 A 모듈B 모듈C


X, Y, Z 공통영역


모듈A의 상황때문에 데이터 X의 길이를 변경했을 때 모듈 A의 프로그래머는 데이터 X 가 모듈 C에서 사용되고 있다는 사살을 모르고 있을 수도 있다.

X의 길이 변경이 모듈 C에 어떤 영향을 초래할지를 모듈  A의 프로그래머는 깊이 생각하지 않고 변경을 실시해 버릴 수도 있다.

이렇게 되면 모듈 C는 생각지도 못한 문제에 휘말릴지도 모른다.

다만 공통 결합 데이터가 다른 모듈에서 사용 가능하다는 점은 공통 결합의 단점인 동시에 장점이기도 하다.

예) 공통 결합을 피하고자 한다면 모듈 간에 교환하는 파라미터 수가 많아진다.

교환하는 파라미터 수가 많아지면 파리미터의 각각의 의미를 정확하게 이해하는 것이 중요하다.

또 모듈 작성시에 많은 파라미터를 작성하는 수고도 생긴다.

이에 비해 공통결합을 사용하면 데이터를 교환하기 위한 파리미터 지정을 회피할 수 있다.

이는 모듈의 작성을 쉽게 만들기 때문에 공통 결합의 단점을 뛰어넘는 장점을 가져올 때가 있다. 현실적으로도 공통 결합은 많이 채용되고 있다.

그러나 애초에 교환하는 파라미터의 수가 많은 것은 적절하지 않는 모듈화가 원인의 대부분을 차지한다.

대부분 모듈의 재설계를 통해 관리하고, 기존 데이터의 위치를 재고함으로써 파리미터의 수를 적게 만들 수 있다.


- 모듈들이 동일한 자료영역(전역변수) 을 공통으로 조회하는 경우

- 동일한 자료 영역내의 오류 발생시 타 모듈로 오류 전파 가능성이 큼



예)

int xyz = 0; 

int doSomething(int delta) {   

      xyz += delta;   

      return xyz;

}



두개의 모듈이 같은 글로벌 데이터를 공유(전역변수 공유)

캡슐화한 모듀을 하나 더 만들어서 데이터를 제공한다.


dbTableName = 'ABC'

//모듈 A,B가 전역변수 dbTableName을 공유

module A {

deletePost();

}

module B {

deleteUser();

}


//해결: 캡슐화된 모듈 DB를 만들어서 사용




3. 외부결합

외부에 선언된 데이터를 공유하는 모듈 결합방식이다

외부에 선언된 데이터란 예를 들어  public으로 선언된 변수를 말한다.


모듈 A 모듈 B

x                   <---------------       사용

외부선언


데이터 공유라는 의미에서는 공통 결합과 비슷한데,  필요한 데이터만 외부에 선언하기 때문에 공통 결합처럼 본래 불필요한 데이터까지 공유하는 일은 없다.

그런 만큼 공통 결합보다는 결합도가 약하다.


- 모듈들이 외부 환경(특수 H/W, 통신프로토콜, OS, 컴파일러 등)과 연관되어 있는 경우



두개의 모듈이 외부에서 도입된 데이터 포맷,  통신 프로토콜, 또는 디바이스 인터페이스를 공유할 때 발생한다.

이는 기본적으로 외부 툴이나 디바이스와의 통신과 관련이 있다.

외부에서 도입된 api에 맞는 모듈을 하나 더 만들어서 해결(어댑터 패턴 형태)


// 두 모듈은 외부에 공개된 API를 통한 통신으로 getData함수와 getName 함수를 구현

module A {

getData()

}

module B {

getName()

}

// 해결방법: 캡슐화된 외부 통신용 모듈을 만들어서 사용


4. 제어결합

호출하는 모듈 쪽에서 호출받는 모듈의 제어를 지시하는 데이터를 파라미터로 넘겨주는 모듈 결합방식이다.

제어 결합에서는 파라미터의 하나로 switch 변수를 넘겨주면서 호출받는 모듈이 그때 수행할 기능을 지시한다.

따라서 호출하는 쪽은 호출받는 모듈의 논리를 알아야 할 필요가 있고 상대를 블랙박스처럼 다룰수 없어 결합도가 강해진다.

호출받는 모듈의 응집도가 논리적 강도가 되어 버린다는 결점도 있다.

다만 데이터를 공유하지 않으므로 공통 결합이나 외부 결합보다는 결합도가 약하다고 할 수 있다.


예)


모듈 A


CALL B(SW)



모듈 B


    SW


a b


- 한 모듈이 다른 모듈의 내부에서 작용하는 논리적 흐름을 제어하기 위하여 제어 플래그(flag)나 정보를 매개변수로 전달하는 경우


예)

void setValue(String name, int value) 

{   

      if ( name.equals("height") ) {

            height = value ;   

       }   

      

      if ( name.equals("width") ) {

            width = value ;

      }

}

 

void setHeight(int arg) {   _height = arg ; }

void setWidth(int arg) {   _width = arg ; }



하나의 모듈이 다른 모듈으로 무엇을 해야하는지에 대한 정보를 넘겨줌으로써 다른 모듈의 흐름을 제어

여러 모듈이 한개(혹은 그 이상)의 기능에 대한 역할(책임)을 짊어지고 있기 때문에 기능 변경시 caller, callee 모두 수정이 필요하다.


오버로딩 혹은 1개 함수를 책임별로 분할하여 해결


module A {

main(){

B.local(true)

}

}


module B {

//isExec 변수에 의해 흐름이 제어됨

local(isExec){

if(isExec) {


}else{


}

}


5.스탬프 결합

공통 영역에 없는 자료구조를 2개의 모듈에서 교환하는 모듈 결합방식이다.

자료구조의 교환은 파라미터를 매개로 수행한다.

다만 스탬프 결합에서는 넘겨주는 자료구조의 일부를 사용하지 않을 때가 있다.

불필요한 데이터까지 교환된다는 점이 결합도를 조금 강하게 만든다.


예)

모듈 A 모듈 B

------자료구조S------>   

CALL B(S)


- 두 모듈이 동일한 복잡 자료구조(배열, 구조체 등)을 매개변수로 전달하여 참조하는 경우


예)


struct {

      int x, y;

} X;

 

int doSomething(struct X p) {

      return p.x + p.y;

}


복잡자료구조(구조체)의 값을 매개변수로 받는경우

자료구조의 형태가 바뀌면 수정이 필요하다.

외부로 공개된 함수의 매개변수를 기본 자료형으로 변경하여 해결


예)

module canvas {

struct point{x, y}

drawPoint(point xy) //point라는 구조체가 의존됨

}


module B {

add(a, b, c) {

canvas.point xy = {1,3}

canvas.drawPoint(xy)

}

}


//해결방법: 의존되는 데이터 구조체등을 기본 데이터로 변경

drawPoint(x, y){

이 안에서는 point 구조체를 사용해도 무관

}

//혹은 인터페이스를 이용



6. 데이터 결합

모듈간의 인터페이스로 스칼라형 데이터 요소만을 파라미터로 교환하는 모듈 결합방식이다.

상대 모듈을 블랙박스화 할수 있으므로 결합도는 약하다.

모듈 간 결합은 명확히 정의된 파라미터로 데이터를 교환하는 '데이터 결합'방식이 가장 좋다고 알려져있다.


예) 


모듈 A 모듈 B


CALL B(X, Y)   ---X-->

                         <--Y---


모듈 A는 모듈 B의 입력으로 X를 넘겨주면 출력으로 Y를 받는다는점만 알고 있을 뿐이다.

모듈 A 입장에서는 모듈B가 X를 어떻게 Y로 변환하는지에 관한 논리를 딱히 알필요가 없다.

즉 모듈 A는 모듈B를 블랙바스로 다룰 수 있고, 모듈 B의  논리변경 때문에 모듈A가 영향을 받을 일이 없다.

모듈B가 입력으로 X를 받아 Y를 출력한다는 점은 강도적으로 보면 기능적 강도에 해당한다.

이는 응집도로 봐도 좋은 결과다.

참고로 스탬프 결합은 넘겨주는 자료구조 중에 일부 데이터만을 사용한다. 

반대로 자료구조의 모든 데이터를 처리한다면 이는 데이터 결합이라고 간주해도 무방하다.


- 한 모듈이 다른 모듈을 호출할 때 필요한 자료만을 매개변수로 전달하여 참조하는 경우


예)


void abc(void) {     

      int a, b, in;     

      int res;     

      scanf("%d %d %d", &a, &b, &in);     

      res = def(a, b, in);

      printf("result = %d\n", res);

}

 

int def(int x, int y, int v) {     

      if(v > 0) {        

            return(x+y);

      } 

      else {        

            return(x-y);

      }

}


기본 자료형의 값을 매개변수로 받는경우

매개변수의 형태가 바뀌면 수정이 필요하다.


module A {

add(a, b)

}


module B {

c = add(a, b)

}



상호 종속되는 모듈은 깨지기 쉽다.

결합이 밀접한 모듈은 서로 종속되며 영향을 미치므로 여러가지 문제가 생긴다.

예) 관계하는 모듈의 변경으로 인해 결합된 모듈에 변경이 필요해지거나 동작에 영향을 미친다.

모듈을 사용하려면 해당 모듈이 종속되고 있는 모듈도 필요하므로 재사용하기 어렵다.

더욱이 모듈의 코드를 단독으로 해독할 수 없다.

여러 관련된 모듈을 읽는다 해도 전역 변수로 연결되어 있다면 동작을 완전히 파악하기 어렵다.


저결합모듈을 지향한다.

모듈의 독립성을 높이려면 모듈 간의 인터페이스는 가능한 한 낮은 단계의 결합도를 지향해야한다.

- 데이터는 가능한 한 파라미터로 넘겨준다.

- 데이터는 가능한 한 전역변수로 두지 않는다. 특정 시점에만 필요한 데이터라면 지역변수로 두도록 한다.

- 넘겨주는 값에 따라 동작이 바뀌는 코드를 작성하지 않는다.

예) 파라미터로 받는 플래그의 내용이 A라면 추가, D라면 삭제하는 식의 함수는 결합도가 높아진다.


결합 대상 모듈의 품질에 주목하자.

밀접한 결합 자체도 문제지만, 본질적으로 불안정한 요소와 밀접하게 결합하는 것이 문제가 된다. 안정적인 라이브러리에 종속한다면야 딱히 문제될일은 없다.

데이터 결합 방식을 맹목적으로 지향하기보다는 결합하려는 대상과 친밀도에 따라 단계를 결정한다.


하이브리드 결합

결합도의 종류로 하이브리드 결합이라는 관점도 있다.

데이터가 상황에 따라 여러 의미를 지니는 상태가 여기에 해당한다.

예) 세율을 반환하는 함수가 있을 때 계산 불가인 상황에서는 오류로 음수값을 반환하는 경우

정상시- 세율

이상시 - 음수값


이 때 상황에 따라 하이브리드 적으로 반환값이 달라지므로 함수를 사용하는 쪽에서 이를 의식하고 사용해야 한다.

하이브리드 결합은 결합도로 다소 높다고 볼 수 있다.


결합도에 관한 착안점을 말하자마녀 결합의 '강도'와'굵기'다.

그러나 결합도에 관한 착안점은 이 뿐만 아니다.

결합에서는 '강도','굵기'와 동시에 '개수', '방향'에도 주의할 필요가 있다.

다른 모듈과 '많이','쌍방향'으로 관계를 가진 모듈은 결합도가 높은 모듈과 똑같은 단점을 갖는다.


멱등성과 안정성

모듈이나 함수가 갖는 성질중에 멱등성과 안전성이라는 개념이 있다.


멱등성

'어떤 조작을 반복해서 수행해도 결과가 같은 것'을 의미하는 수학용어로 같은 함수를 반복해서 호출해도 반드시 같은 결과를 얻을 수 있다.

예) 0의 곱셈은 멱등이다.

3 * 0, (3*0 )* 0 의 결과는 모두 0이다.

절대값 계산 역시 멱등 abs(-3), abs(abs(-3))의 결과는 모두 3


안전성

'조작 대상의 상태를 변화시키지 않는 것'을 의미

상태에 변화를 미치는 것을 부작용이라고 부르므로, 안전은 '조작 대상의 상태에 부작용이 없는 것'이라고 바꿔 말할 수 있다.


서버가 안전성과 멱등성을 갖는다면 클라이언트는 더 블랙바스적으로 서비스를 사용할 수 있다.

HTTP 요청과 같은 비교적 오류가 발생하기 쉬운 네트워크 환경에서 사용하는 프로토콜에서는 특히 유용한 성질이다.

예) HTTP의 GET 요청은 멱등성과 안전성이 요구된다.

이 조건을 충족할 경우 GET 요청을 보내고 응답이 돌아오지 않는다면 한 번 더 요청을 보내면 될 뿐이다.

안정적으로 같은 결과를 얻을 수 있다는 점이 보증된 데다, 동시에 먼저 들어오는 요청이 처리되고 있었다 해도 서버에 실질적인 영향은 미치지 않으므로 안전하다.


출처 - 프로그래밍의 정석

반응형

'issue & tip' 카테고리의 다른 글

인터페이스 기반 프로그래밍  (0) 2018.07.02
토비의 봄 TV 재사용성과 다이나믹 디스패치 그리고 더블디스패치  (0) 2018.06.01
응집도  (0) 2018.05.09
파일 입출력  (0) 2018.04.25
래퍼클래스와 제네릭  (0) 2018.04.24