Web/[JS] Common

응집도와 결합도

ihl 2021. 6. 12. 22:33

응집도와 결합도

  프로그래밍이란 문제를 정의하고 해결해나가는 과정이다. 만약 문제가 너무 복잡하고 큰 문제라면 어떻게 해야할까? 일반적으로 문제를 작은 부분으로 쪼개어 하나씩 풀어나갈 것이다. 이 때 문제를 작은 부분으로 쪼개나가는 것을 모듈화라고 한다. 

 

  모듈은 프로그램의 일부로, 독립적인 기능을 갖는 단위이다. 따라서 독립적으로 컴파일 가능한 프로그램 혹은 하나의 함수도 모듈이 될 수 있다. 일반적으로 모듈의 독립성이 높을수록 좋은 모듈이라고 한다. 일반적으로 독립성이 높은 모듈이 재사용성이 높고 코드의 이해/수정이 용이하기 때문이다. 이 때 모듈의 독립성은 결합도와 응집도로 측정한다.

 

  응집도는 한 모듈 내의 구성요소들 간의 연관 정도를 의미하고, 결합도는 모듈과 모듈 간의 의존 정도를 의미한다. 응집도는 강할수록, 결합도는 느슨할 수록 독립성이 높은 모듈이다. 이들에 대해 자세히 알아보자.

 

1. Cohesion

응집도: 모듈 내의 구성 요소간 서로 연관되어 있는 정도

 

  응집도란 한 모듈 내의 구성 요소 간의 밀접한 정도를 의미한다. 한 모듈이 하나의 기능(책임)을 갖고있는 것은 응집도가 높은 것이고, 한 모듈이 여러 기능을 갖고 있는 것은 응집도가 낮은 것이다. 응집도가 높은 모듈은 기능을 수정할 때 관련 내용이 하나의 모듈에 모여있으므로 코드를 이해하기도 쉽고, 수정한 후 관련 없는 다른 모듈에겐 영향을 주지 않아 코드의 유지보수에 유리하다. 즉, 응집도가 높을 수록 독립성이 높은 모듈이다.

 

기능적 응집 > 순차적 응집 > 교환적 응집 > 절차적 응집 > 시간적 응집 > 논리적 응집 > 우연적 응집

 

  어떤 기준으로 요소들이 모여있는지에 따라 모듈의 응집도를 분류할 수 있다. 기능적 응집이 가장 강한 응집도를 가지므로 독립성이 높은 모듈이고, 우연적 응집은 약한 응집도를 가지므로 독립성이 낮은 모듈이다.

 

 1-1) Functional Cohesion

export const add = (a, b) => {
  return a + b;
}

  모듈 내의 모든 요소들이 하나의 기능을 수행하기 위해 구성된 것이 기능적 응집이다. 응집도가 가장 높다. 예를 들어 단일 기능을 갖는 함수 모듈이 있다.

 

 1-2) Sequential Cohesion

export const getData = (info) => {
  const key = getKey(info); // 결과 key
  return getDataFromFile(key); // key가 입력이됨
}

   한 요소의 출력이 다른 요소의 입력으로 사용되는 경우는 순차적 응집이다.

 

 1-3) Communication Cohesion

export const bloodTest = {
  getGroup: function (blood) {  /* A,B,AB,O */ },
  getAllergy: function (blood) {  /* 알레르기 검사 */ },
  getAnemia: function (blood) {  /* 빈혈 검사 */ },
}

   모든 요소들이 동일한 입/출력 데이터를 사용하여 서로 다른 기능을 수행하는 경우는 교환적(통신) 응집이다. 같은 입력을 사용하는 요소를 하나의 모듈로 구성하거나 같은 출력을 만드는 요소를 하나의 모듈로 구성한 것으로 순서는 중요하지 않다.

 

 1-4) Procedural Cohesion

   모듈 내의 요소가 순차적으로 수행되는 경우는 절차적 응집이다. 이 때 순차적 응집과 달리 한 구성 요소의 출력이 다음 구성요소의 입력으로 사용되는 것은 아니다.

 

 1-5) Temporal Cohesion

export const init = {
  initVariables: function () { /* 변수 초기화 */ },
  render: function (v) { /* 화면 그리기 */ },
  connect: function () {  /* 통신 연결 */ }
};

   각 요소가 순서에 상관없이 특정 시점에 시행되는 경우 시간적(일시적) 응집이라 한다. 모듈 내 구성요소들의 기능은 각자 다르지만 같은 시간대에 함께 실행된다. 프로그램 최초 실행 시 실행되는 초기 값 설정모듈, 오류 발생 시 오류 로그를 개발자에게 전달하는 모듈이 이에 속한다.

 

 1-6) Logical Cohesion

export const converter = {
  numToStr: function (n) { return '' + n; },
  strToNum: function (s) { return +s; },
  timeConverter: function (date) { /* 시간변환 로직 */ },
};

   비슷한 기능을 수행하거나 관련된 임무가 있는 요소를 하나의 모듈로 구성한 경우는 논리적 응집이다. 요소간의 순서는 상관 없으며 비슷한 기능을 수행하지만 서로 밀접한 관계가 있지는 않다.

 

 1-7) Coincidental Cohesion

   모듈 내의 각 요소들이 아무런 관련이 없다면 우연적 응집이다. 모듈 간의 유사성이 없으며 응집도가 가장 낮다.

 

2. Coupling

결합도: 모듈과 모듈 간의 상호 의존 정도

 

  결합도란 한 모듈이 다른 모듈에 의존하는 정도를 의미한다. 결합도가 강하다는 것은 하나의 모듈을 변경하면 다른 모듈도 변경해야함을 의미이다. 따라서 결합도가 느슨할수록 모듈을 다른 프로그램에 재사용 하기도 용이하고, 코드를 수정할 때도 다른 모듈에 영향을 주지 않는다. 즉, 결합도가 느슨할수록 독립성이 높아 좋은 모듈이다.

 

데이터 결합 < 스탬프 결합 < 제어 결합  < 공통 결합 < 내용 결합

 

  모듈 간의 연결 방식에 따라 결합도를 분류할 수 있다. 데이터 결합도가 결합이 가장 느슨한 모듈이며, 내용 결합도가 가장 결합이 강한 모듈이다

 

 2-1) Data Coupling

   모듈간에 매개변수를 통해 순수한 자료형 데이터(Primitive)만 주고받는 방식이 데이터 결합이다. 따라서 모듈 간의 관계가 단순하고 결합도가 가장 낮다.

 

 2-2) Stamp Coupling

Person p = new Person("ihl", "010-1111-11111");
Call(Object.Assign(p));
function Call(person) {
  /*person.phone 만 사용한다 */
}

   모듈 간 배열 혹은 객체와 같은 자료구조(Reference)가 전달되는 방식은 스탬프 결합이다. 스탬프는 정보의 일부만 찍을 수 없고, 일부의 정보만 바뀌어도 스탬프를 다시 만들어야 한다. 이처럼 스탬프 결합은 두 모듈 사이에 필요한 데이터만 주고받는 것이 아니라 필요 없는 데이터까지 전체를 주고받는다. 또한 데이터 하나가 변경되면 관련된 모듈들도 변경되야 할 수 있다. (모듈 간의 인터페이스로 배열, 객체가 전달되는 경우인데 JavaScript에는 인터페이스가 없으므로 Object.Assign()으로 대체하여 사용하였다.)

 

 2-3) Control Coupling

const Person = {
  name: 'ihl',
  rank: 'silver',
}

const calculateSale = (price, rank) => {
  if (rank === 'silver') { return price * 0.9; }
  else if (rank === 'gold') { return price * 0.8; }
  else { return price; }
}

   한 모듈이 다른 모듈의 논리적 흐름을 제어하는 요소(Flag)를 전달하는 경우는 제어 결합이라 한다. 제어 결합은 정보은닉을 위배하는 결합으로, 한 모듈이 다른 모듈 내부에 관여하여 관계가 복잡해진다.

 

 2-4) Common Coupling

   여러 모듈이 하나의 데이터를 참조하는 경우 공통 결합이라 한다. 예를 들어 두 모듈이 같은 전역변수를 참조하는 경우 공통 결합이라 한다. 전역변수의 변경에 따라 두 모듈도 영향을 받는다. 이와 유사하게 외부 결합이라는 개념도 있다. 외부 결함은 공통 결합과 유사하나 공통 데이터(프로토콜 등)가 외부에 있다는 특징이 있다.

 

 2-5) Content Coupling

class Person {
  constructor(name) {
    this.name = name;
    this.age = 0;
  }
}

const updateYear = (name) => {
  const person = new Person(name);
  person.age = person.age + 1; // 직접 객체의 상태 변경
}

   한 모듈이 다른 모듈의 데이터를 직접 참조하는 경우이다. 이 경우 상대 모듈의 데이터를 직접 변경할 수 있다. 내용 결합은 모듈 간에 인터페이스를 사용하지 않고 직접 참조한다. (JavaScript는 인터페이스와 추상화를 제공하지 않으므로 내용결합이 자연스럽게 되어버리는 것이 아닐까라는 생각이 들었지만 찾아보니 JavaScript는 동적타입언어이므로 그 자체로 느슨한 결합도를 갖는다는 정보를 보았다/stackoverflow/. 위 코드에서 updateYear에서 person 인스턴스를 받도록 변경/Dependency Injection/하면 updateYear 모듈에 Person 클래스는 필요 없어지기 때문. 즉, JS 에서는 Duck Typing으로 타입을 판단하므로 반드시 각 모듈에서 다른 모듈을 import할 필요가 없다 따라서 모듈간 결합도가 약하다라는 의미인 것 같다.)

 


지식백과: https://terms.naver.com/entry.naver?docId=3532985&cid=58528&categoryId=58528&expCategoryId=58528

응집도 결합도: https://madplay.github.io/post/coupling-and-cohesion-in-software-engineering

JS와 느슨한 결합: https://stackoverflow.com/questions/42667848/javascript-interfaces-loose-coupling

JS 느슨한 결합 예시코드: JavaScript examples: No coupling, Loose coupling, Tight coupling · GitHub

+ JS abstract factory: https://ui.toast.com/weekly-pick/ko_20150522