좋은 소프트웨어 설계
좋은 소프트웨어는 객체 내부 상태를 캡슐화하고, 객체 간 메시지를 통해서 상호작용한다. 밀접하게 연관된 작업만 수행하고, 연관이 없는 작업은 다른 객체에게 위임한다. 내부 구현을 감추고 외부로부터 보호하며, 외부와 협력을 할 때는 인터페이스에 의존해야 한다.
자신이 소유하고 있지 않는 데이터로 작업을 처리하면 안 된다. 자신이 소유한 데이터만 책임하며, 메시지를 통해 협력하라.
좋은 설계를 만들기 위해서 생각해야 할 것들은 위 내용과 같다. 이 내용에서 응집도, 결합도, 캡슐화에 관한 내용을 볼 수 있다. 밀접하게 연관된 작업만 수행하는 것은 응집도가 높다고 할 수 있다. 자신의 데이터만 책임지고, 메시지를 통해 협력하는 것은 다른 모듈끼리 결합도가 낮다고 할 수 있다. 마지막으로 내부 구현을 감추고, 인터페이스로 외부와 협력을 하는 건 캡슐화를 통한 DIP(의존 역전의 원칙)가 지켜진 모습이다.
응집도와 결합도가 어떤 점에서 영향을 주는 것일까?
먼저 응집도와 결합도를 하나씩 알아보고, 캡슐화와 관계를 알아보자.
응집도 Cohension
모듈에 포함된 내부 요소들의 연관 정보를 말한다. 하나의 모듈 안에서 요소들이 얼마나 응집되어 있는가를 판단하는 요소로, 응집도가 높을수록 좋은 소프트웨어라고 한다.
하나의 목적을 위해서 서로 협력하는 것. 어떤 수정이 있을 때, 그 기능을 책임지고 있는 모듈 안에서만 수정이 이루어져야 한다. 만약 기능 하나를 수정하기 위해서 다른 모듈에서도 수정이 발생하면 응집도가 낮다고 한다.
1. 단일 책임 원칙(SRP)을 지키는 응집도
응집도를 높이기 위해서는 SRP를 지켜야 한다. 한 클래스는 하나의 책임만 가지고 있다. 여러 책임이 하나의 클래스에서 기능하면 SRP도 지키지 않았고, 응집도도 낮다. 기능 분리가 제대로 된다면, 한 기능에 대해 변경이 있을 때 다른 클래스에서 수정은 없을 것이다.
클래스의 책임에 맞게 기능을 제대로 분리시켜 응집도를 높이자.
2. 응집도의 종류와 강도 차이
응집도 높음 → 응집도 약함 | ||||||
기능적 응집도 | 순차적 응집도 | 교환적 응집도 | 절차적 응집도 | 시작적 응집도 | 논리적 응집도 | 우연적 응집도 |
위 표는 오른쪽으로 갈수록 응집도가 약해지는 모습을 표현한다.
기능적 응집도가 제일 응집도가 높고, 우연적 응집도가 제일 응집도가 약하다.
세기 | 종류 | 내용 |
강함 ↓ 약함 |
기능적 응집도 (Functional Chohesion) |
모든 기능이 단일 목적을 위해 수행 |
순차적 응집도 (Sequential Cohesion) |
한 활동에서 나온 출력 값을 바로 이어서 다음 입력으로 사용 앞의 값이 있어야 다음 모듈을 실행 → 순서 O |
|
교환적 응집도 | 통신적 응집도 (Communication Cohesion) |
입력과 출력에서 동일한 기능을 사용하는 것 한 기능의 반환값(출력)이 다른 기능의 입력이 되는 경우 → 순서 X |
|
절차적 응집도 (Procedural Cohesion) |
여러 기능을 순차적으로 수행하는 경우 | |
시간적 응집도 | 일시적 응집도 (Temporal Cohesion) |
순서에 상관없이 특정 시점에 반드시 수행되는 경우 Exception 에러를 보낼 때가 여기에 포함 |
|
논리적 응집도 (Logical Cohesion) |
논리적으로 비슷한 기능을 수행하지만, 관계는 밀접하지 않은 상태 | |
우연적 응집도 (Coincidental Cohesion) |
서로 관련 없는 요소로 구성된 경우 |
결합도 Coupling
다른 모듈에 대해 얼마나 많은 지식을 가지고 있는지 나타내는 척도를 의미한다. 응집도는 하나의 모듈 내부에서 관계라면, 결합도는 모듈과 모듈 사이에서의 관계다. 응집도와 반대로 결합도는 낮을 수록 좋은 설계라고 한다.
응집도를 설명할 때, 기능 변경을 예시로 들었다. 하나의 기능을 변경하고 싶은데 여러 모듈에서 변경이 발생하면 응집도가 낮아 좋지 않다. 이는 결합도에도 적용된다. 모듈과 모듈은 각각의 책임을 담당하고 있다. 하나의 책임에 대해서 변경을 하는데, 다른 책임을 담당하는 모듈도 변경을 해야 한다면 두 모듈은 서로 밀접하게 연관되어 있다는 의미가 된다.
모듈 사이의 결합도가 높기 때문에 하나를 수정하는데, 다른 모듈까지 수정해야 하는 것이다. 다른 모듈의 자세한 부분을 알고 있지 않고, 꼭 필요한 지식만 가지고 있는 상태로 만들어야 결합도를 낮출 수 있다.
1. 개방 폐쇄 원칙(OCP)를 지키는 결합도
결합도가 낮은 클래스는 OCP를 지키고 있다. OCP는 수정을 할 때 다른 모듈에 영향을 주지 말아야 한다는 원칙이다.
모듈 사이에 결합도가 높아 서로의 내부가 관련이 있을 때, 하나의 모듈에서 수정이 생기면 어쩔 수 없이 다른 모듈에서도 수정을 해야 한다. 결합도가 높을 때는 OCP가 전혀 지켜지지 않는다.
만약 결합도가 낮다면(좋은 결합도라면) 모듈 하나를 수정할 때 다른 모듈은 영향을 받지 않는다. 각 모듈 사이의 연관이 적어, 수정을 해도 다른 모듈은 그대로 유지할 수 있다.
2. 결합도의 종류와 강도 차이
결합도 약함 → 결합도 강함 | |||||
자료 결합도 | 스탬프 결합도 | 제어 결합도 | 외부 결합도 | 공통 결합도 | 내용 결합도 |
응집도와 반대로 결합도는 오른쪽으로 갈수록 강한 모습이다.
결합도가 제일 낮은 것은 자료 결합도, 제일 강한 것은 내용 결합도로 결합도는 낮을 수록 좋다.
세기 | 종류 | 내용 |
약함 ↓ 강함 |
자료 결합도 (Data Coupling) |
모듈끼리 단순히 데이터를 주고 받는 형태 |
스탬프 결합도 (Stamp Coupling) |
동일한 자료구조를 참조하는 형태 - 자료구조 형태가 변경되면 그것을 참조하는 모듈에 영향을 준다. |
|
제어 결합도 (Control Coupling) |
논리적 흐름을 제어하는 요소를 전달하는 경우 - 전달되는 값에 따라 내부 로직의 처리(계산)가 달라진다. |
|
외부 결합도 (External Coupling) |
외부에 있는 다른 모듈의 데이터를 참조하는 경우 - 특수 H/W, 통신 프로토콜, OS, 컴파일러 등 외부 환경과 연관될 때 - import로 가져온 외부 모듈의 반환 값을 참조할 때 |
|
공통 결합도 (Common Coupling) |
여러 개의 모듈이 하나의 공통 데이터 영역을 사용 → 전역 변수 | |
내용 결합도 (Content Coupling) |
다른 모듈의 내부 기능과 데이터를 직접 참조해 사용하거나 수정 |
캡슐화와 관계
1. 캡슐화
캡슐화는 객체 지향 설계에 중요한 부분으로 객체 지향에 대해 설명할 때 여러 유형에서 등장한다.
객체 내부 구현을 외부로부터 감추는 것이다. 주의할 점은 객체는 단순히 데이터를 제공하는 역할이 아닌, 협력에 참여해 책임하는 기능이라는 걸 생각해야 한다.
2. 응집도& 결합도는 변경에 관련이 있다.
좋은 소프트웨어 설계는 오늘의 기능을 수행하면서 내일의 변경을 수용할 수 있어야 한다.
하나를 변경할 때, 해당 모듈 전체가 변경되면 모듈 내부의 응집도가 높고, 다른 모듈에 영향 없이 그 모듈만 변경되면 모듈 사이 결합도는 낮다. 다른 모듈에 영향을 주지 않으니 과거의 기능을 수행할 수 있고, 해당 모듈 자체에서 변경이 생겼으니 확장에 열려있다.
응집도와 결합도를 설명할 때 기능 추가, 기능 수정 등 코드 변경을 말하면서 예시를 들었다.
응집도와 결합도는 변경에 대해서 깊은 연관이 있다.
3. 캡슐화는 변경과 어떤 연관이 있을까?
의존 역전의 법칙(DIP)은 다른 모듈을 참조할 때, 구현체가 아닌 추상체에 의존해야 한다는 원칙이다.
구현체는 감추고, 외부에서 참조는 인터페이스를 통한다. ⇒ 캡슐화
구현과 인터페이스를 분리하는 기준은 변경의 정도로 볼 수 있다. 변경 될 가능성이 많은 것은 구현부로, 상대적으로 안정적인 것은 인터페이스로 분리한다. 그리고 구현 세부사항은 인터페이스 뒤로 캡슐화 한다.
위 글을 보면 [좋은 인터페이스를 설계하는 방법]에서 캡슐화를 통해 추상화를 해야 한다는 내용이 있다.
변경의 정도를 잘 분리하여 구현과 인터페이스로 나누고, 인터페이스를 통해서 캡슐화를 진행한다. 캡슐화도 변경에서부터 시작하며, 외부 접근으로부터 내부를 보호한다는 것 또한 OCP와 연관지어 봤을 때 변경을 위해서 하는 행위로도 볼 수 있다.
4. 캡슐화를 지킨 설계에서 응집도와 결합도
캡슐화를 잘 지킨 소프트웨어가 있다. 외부 접근으로부터 보호하기 위해 인터페이스를 사용한다.
구현-구현이 직접 닿지 않아, 하나의 구현체에서 변경이 생겨도 다른 구현체에는 영향을 주지 않는다. 각 모듈마다 책임이 분리되어 있어 우리 모듈에서 변경이 일어나면 모듈 내부에서만 변경된다.
다른 모듈에 있는 '수정하고 싶은 기능'을 찾지 않아도 된다. 그저 '기능을 하는 모듈'만 전체적으로 수정하면 된다.
⇒ 결합도가 낮고, 응집도가 높다.
객체 지향 설계에서 각 특징은 서로 연관되어 있다.
OOP를 알아볼 때도, SOLID를 알아볼 때도 각 특징은 자기만 설명하지 않는다. 서로서로가 연관되어 있고, 서로 영향을 준다. 응집도와 결합도를 알아볼 때도 그 개념만 알아보지 않고, SOLID와 연관되는 특징, OOP와 연관되는 특징을 알아보았다. 서로 영향을 주는 만큼 객체 지향 설계를 제대로 하기 위해서는 그 개념을 제대로 알아야 한다. 하나만 알고 다른 것을 알지 못하면 제대로 된 설계를 하기 힘들어지고(하지 못한다x 힘들다o), '왜' 그렇게 하는지 알지 못한 채로 시키는 것만 하게 된다. 스스로 좋은 객체 지향 설계를 하기 위해서 각 유형별로 공통점을 찾아낼 수 있어야 한다.
reference
조영호. 「오브젝트: 코드로 이해하는 객체지향 설계」. 위키북스(2019).
인파. 「객체의 결합도 & 응집도 의미와 단계 총정리 」. Inpa Dev.
https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EA%B2%B0%ED%95%A9%EB%8F%84-%EC%9D%91%EC%A7%91%EB%8F%84-%EC%9D%98%EB%AF%B8%EC%99%80-%EB%8B%A8%EA%B3%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC
LIB. 「결합도 & 응집도」. Life Is Beautiful. https://m.blog.naver.com/gkenq/221001895996
'Language > JAVA' 카테고리의 다른 글
[JAVA] 컬렉션 프레임워크 (Java Collection Framework:JCF) 간단하게 알아보기 (0) | 2025.01.20 |
---|---|
[JAVA] 예외 처리(Exception Handling) 하는 이유와 방법 알아보기 (0) | 2025.01.15 |
[자바] 자바 가상 머신, JVM(Java Virtual Machine) 자세히 이해하기 (0) | 2025.01.13 |
객체 지향 5대 원칙 - SOLID 이해하기 (0) | 2025.01.10 |
객체 지향 프로그래밍(Object-Oriented-Programming) 자세히 이해하기 (0) | 2025.01.08 |