높은 응집도 & 낮은 결합도. 객체 지향적 프로그래밍을 실무 단계에서 적용하면, 이후 유지보수와 리팩토링과 함께 따라오는 표현들이다. 이전 나의 프로그래밍은 주먹구구식 프로그래밍이었다. 어떻게서든 돌아가게만 만들면 되었고, 해당 프로그램이 돌아가면 그 이후로 다른 변경없이 고정된 입력값과 출력값에 만족하곤 그대로 마무리 지었다. 


하지만 위와 같은 방식으로 하게 된다면 이후 생각지도 못한 변수나 유지보수에 장애가 생기기 마련이다. 결국은 기능을 분할하고 추상화하는데 있어서 소프트웨어 개발에 그런 염두를 두지 않았기 때문에 장애를 해결하는데는 많은 시간과 노력을 가할 수 밖에 없다. 


따라서 Loose Coupling 과 High Cohesion 은 매우 중요하다. 근래들어 느끼는 거지만 기술적인 부분을 쌓는 것도 중요하지만 기본과 기초가 매우 중요함을 깨닫는다.



- 응집도 (Cohesion)

객체 지향 설계에서 응집도란, 하나의 클래스가 내부의 요소들이 어떻게 설계되었는지에 대한 모든 것을 의미한다. 단일의 클래스 내부에 있는 기능적인 요소들이, 어느 특정한 목표행위에 대해 얼마나 상호 연관되어 있는 정도(척도)를 따진다. 단일의 클래스에 집중하여 기능적인 요소들을 응집시킬수록 해당 클래스의 응집도는 올라간다.


    • 정보은닉 개념을 적용한 모듈의 기능 연관성을 측정하는 척도

    • 응집도가 높아지면 다른 모듈과의 의존도가 낮아지고 독립성이 강해짐

    • 한 모듈 내에 필요한 함수와 데이터들의 친화력을 측정하는데 사용

    • 응집도가 높아지면 재 사용성이 향상되고 품질도 향상되는 효과

    • 모듈 간 결합도를 최소화하여 응집도를 높이고 유지보수 용이


높은 응집도를 가지는 클래스는 낮은 응집도를 가지는 클래스보다 더욱 유지보수하기 쉽다고 한다. 왜? 스스로가 가지는 기능적인 요소들이 유기적으로 연관되어 있기 때문에 다른 클래스들을 수정하지 않고 해당하는 하나의 클래스만 수정하면 되기 때문이다.


여기서 중요한 것은 응집도를 설명하는 시점에서 하나의 클래스는 하나의 모듈이라고 생각하자. (class = module) 따라서 응집도가 높은 클래스(모듈)은 어느 기능을 하는 것인지에 대한 아이덴티티가 명확하다. 아래는 낮은 응집도 그리고 높은 응집도에 대한 예시이다. 낮은 응집도를 가지는 클래스는 기능의 연관성이 Letter라는 것에 맞추어져 있지만 사실상 하나에 기능에 충실하지 못하고 있다. 반면에 높은 응집도는 하나의 기능에 충실한 것을 볼 수 있다.




작은 프로젝트 단위에서는 응집도의 필요성을 느끼지 못할수도 있으나 프로젝트의 규모가 커지고 넓어지면 이후 유지보수에 도움이 될 것이라 생각한다.



- 결합도 (Coupling) 

결합도는 하나의 요소가 다른 요소과 연관이 있는 정도(척도)를 일컫는다. GeeksforGeeks에서는 약간 과격한 예시를 들었는데, 네가 얼굴(Skin)을 바꾸려면 몸(Body)도 바꾸어야 하는 것으로 예시를 들었다. 얼굴과 몸은 뗄 수 없는 결합도가 아주 높은 것들?이기 때문이다. 여튼 결론은 우리가 어느 한 요소에 변경을 가하면 그와 연결된 다른 요소에 얼마나 영향력을 끼치는가이다. 아래의 코드를 보자


1
2
3
4
5
6
7
8
9
10
11
12
13
class Subject{
  Topic t = new Topic();
  
  public void startReading(){
    t.understand();
  }
}
 
class Topic{
  public void understand(){
    System.out.println("Tight Coupling Concept");
  }
}
cs


위의 내용을 살피면, Subject 클래스는 Topic 클래스에 종속된다. 왜냐하면 Topic 클래스의 메소드인 understand()의 메소드 명이 변경되면 Subject 클래스의 startReading() 메소드의 내의 호출하는 메소드 명 또한 변경되어야 하기 때문이다. 클래스간의 상호의존성이 커질수록 결합도는 올라갈 수 밖에 없음며, 결과적으로 어떤 변경을 가하게 되면 종속적인 클래스들 또한 변경이 가해질 수 밖에 없다.


중요한 것은 결합도가 낮춰졌을때 비로소 두 오브젝트 간의 관계또한 느슨해져, 변경이 발생하는 경우 연결되는 다른 오브젝트에게 영향이 미치지 않는다.


반면에, 다른 경우를 살펴보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface Topic{
    void understand();
}
 
class Topic1 implements Topic {
    @Override
    public void understand(){
        System.out.println("Got it");
    }
 
class Topic2 implements Topic {
    @Override
    public void unserstand(){
        System.out.println("understand");
    }
 
public class Subject {
    public static void main(String[] args){
        Topic t = new Topic1();
        t.understand();
    }
}
cs


위의 경우를 살펴보면, 객체 간의 긴밀하게 연결되어 있는 결합성을 인터페이스의 활용을 통해서 느슨하게(Loose) 해결하고 있다. 스프링 프레임워크에서는 오브젝트간의 높은 결합도를 해결하기 위해서 POJO 프로그래밍 방식을 이용한다. POJO 와 관련된 글은 여기 글을 참고하길 바란다. 따라서 POJO를 통하여 의존성 주입 메커니즘을 이용하고 이로 인해 결합도는 느슨하게 하고 있다.


Topic1 과 Topic2 는 서로 Loose Coupling 되어있다. 둘 사이를 인터페이스가 연결해주고 있는 셈이다. 그리고 단단하게 연결된 결합도와 느슨하게 연결된 결합도를 그림으로 나타내면 아래와 같다. 



두 모양 중에 오른쪽의 결합도 모양을 갖춘 오브젝트가 더 많은 이점이 존재한다. 우선적으로 프로젝트의 규모가 커지게 되면, 변경사항이 생기는데 그 변경사항의 범위를 줄일 수 있으며 일부만 영향을 받게할 수 있기 때문이다. 가장 처음에 말했듯이 프로젝트가 한번 구현 끝내고 이후에 유지보수를 하지 않는 것은 일회용품을 만들겠다는 소리이다.


더불어서 왼쪽보다 오른쪽이 테스터하기에 더 적합하다. 왼쪽은 서로의 오브젝트가 긴밀하게 결합되어 있기 때문에 작은 단위의 테스트라도 많은 과정을 거치는 반면에 오른쪽은 그렇지 않다. 결과적으로 결합도를 낮추었기 때문에 변경이 용이하다.



Posted by doubler
,