객체지향 프로그래밍(OOP)에서 가장 중요한 두 가지 원칙은 바로 응집도(Cohesion)와 결합도(Coupling)입니다.
응집도는 클래스 내부의 모든 메서드들이 서로 얼마나 밀접한 관계를 맺고 있는지를 말합니다. 메서드들이 동일한 인스턴스 변수와 매개변수를 공유하며 하나의 공동 목표를 향해 함께 작동하고 있나요? 아니면 각 메서드가 서로 동떨어져 있는 느낌인가요?
결합도는 한 클래스가 다른 클래스에 얼마나 의존하고 있는지, 즉 시스템의 나머지 부분과 얼마나 "엉켜 있는지", 그리고 이 클래스가 독립적으로 사용될 수 있는 능력이 어느 정도인지를 나타냅니다.
이 두 개념은 코드베이스의 모든 클래스를 특정한 관점에서 바라볼 수 있게 해줍니다. 이러한 렌즈를 통해 클래스 설계가 얼마나 견고한지, 그리고 어디를 수정해야 할지 파악할 수 있습니다.
가장 좋은 결과를 얻으려면 높은 응집도(high cohesion)와 낮은 결합도(low coupling)를 지향해야 합니다.
응집도 예시
낮은 응집도는 피자 위에 어울리지 않는 토핑을 올려놓은 것과 같고, 높은 응집도는 모든 재료가 제자리에 있는 피자처럼 자연스럽습니다.
느낌은 알 수 있지만 측정하기는 어렵습니다. 클래스의 응집도를 숫자 하나로 알려주는 도구는 없으니까요. 그래서 쉽게 이해할 수 있도록, 아주 명확하게 드러나는 코드 예제를 보여드리겠습니다.
class Library def lend_book end def return_book end def make_coffee end end
이 예제에서 make_coffee 메서드는 유독 눈에 띕니다. 설령 이 도서관에 카페테리아가 있더라도 Library 클래스가 커피를 만드는 일은 말이 되지 않습니다 :)
카페테리아는 어떤 책이 있는지, 책을 어떻게 대출하는지 같은 정보가 전혀 필요 없습니다.
마찬가지로 도서관도 커피가 얼마나 남았는지, 커피를 만드는 방법을 알 필요가 없습니다.
이것이 바로 낮은 응집도(LOW cohesion)입니다.
물론, 항상 이렇게 명확하지는 않습니다. 때로는 주의 깊게 메서드 이름 너머를 들여다봐야 합니다.
- 이 메서드들은 실제로 무슨 작업을 하고 있는가?
- 어떤 데이터를 다루고 있는가?
- 어떤 객체들과 협력하고 있는가?
이런 질문들을 던져보면 클래스의 응집도 수준을 가늠할 수 있습니다.
낮은 응집도는 어떻게 고칠까요?
이 클래스에 속하지 않는 메서드들을 다른 클래스, 보통 새로운 클래스로 추출(extract)하면 됩니다.
결합도 예시
이번에는 결합도를 살펴보겠습니다.
class ShoppingCart
attr_accessor :items
def initialize
@items = []
end
end
class Order
def process_order(cart)
cart.items.map(&:price).inject(:+)
end
end
이 예제에서 Order 클래스는 ShoppingCart 클래스와 강하게 결합되어 있습니다. items 변수의 존재까지 너무 많이 알고 있고, 그 데이터로 직접 계산을 수행하기 때문입니다.
만약 ShoppingCart의 items 구현 방식을 변경하면, Order 클래스도 함께 수정해야 합니다.
좋지 않죠!
다음과 같이 수정할 수 있습니다:
class ShoppingCart
attr_accessor :items
def initialize
@items = []
end
def calculate_total
items.map(&:price).inject(:+)
end
end
class Order
def process_order(cart)
cart.calculate_total
end
end
계산 로직을 원래 속해야 할 곳으로 옮김으로써 결합도를 낮추었습니다.
비유를 들자면, 배터리가 고정된 휴대폰이 높은 결합도(high coupling)입니다.
반면 배터리를 교체할 수 있다면 그것이 낮은 결합도(low coupling)입니다.
단, 어느 정도의 결합은 여전히 필요합니다. 배터리가 없으면 휴대폰이 작동하지 않듯이, Order 클래스도 고객에게 청구할 금액을 알지 못하면 동작할 수 없습니다.
정리
지금까지 매우 중요한 두 가지 OOP 원칙인 응집도와 결합도에 대해 알아보았습니다. 이 두 개념은 클래스 안의 메서드들이 얼마나 잘 협력하는지, 그리고 클래스가 시스템의 나머지 부분으로부터 얼마나 독립적인지 파악하는 데 도움을 줍니다.
바로 지금 진행 중인 프로젝트에 적용해 볼 수 있습니다. 프로젝트를 열어 여러분의 클래스 몇 개를 살펴보세요. 오늘 배운 내용을 활용하면 어떻게 개선할 수 있을까요?
읽어주셔서 감사합니다! 아직 구독하지 않으셨다면 글을 공유하고 뉴스레터를 구독하는 것도 잊지 마세요 :)