응집도와 결합도

 💡 객체지향 & 소프트웨어에서 중요한 응집도와 결합도에 대하여 학습하였습니다.

소프트웨어 모듈 독립성

  • 소프트웨어에서 크고 복잡한 문제가 발생했을 때, 문제를 작은 부분으로 쪼개어서 하나씩 풀어나가야 합니다.
  • 이런 경우 큰 문제를 작은 부분으로 쪼개어 해결하는 것을 모듈화라고 하며, 소프트웨어를 각 기능별로 나누어진 소스 단위를 뜻합니다.
  • 독립적으로 컴파일 가능한 프로그램 혹은 하나의 함수나 클래스도 모듈이 될 수 있습니다.
  • 좋은 소프트웨어 일수록 모듈의 독립성이 높다라고 표현할 수 있습니다.
  • 좋은 모듈화는 목적에 맞는 기능만으로 모듈을 나누게 됩니다.
  • 각각의 모듈은 주어진 기능만을 독립적으로 수행하기 때문에 재사용성이 높고 코드의 이해와 수정이 용이합니다.
  • 해당 모듈을 수정하더라도 다른 모듈에 끼치는 영항이 적으며, 오류가 발생하더라도 기능 단위로 잘 나누어 져 있기 때문에 손쉽게 문제를 발견해 해결할 수 있기도 합니다.
  • 이러한 모듈의 독립성은 모듈의 결합도  응집도 의 기준 단계로 측정됩니다.

결합도(Coupling)

  • 결합도는 모듈간의 상호 의존 정도 또는 연관된 관계의 끈끈한 정도를 의미합니다.
  • 결합도가 높은 클래스는 다른 클래스와 연관 관계가 높으며, 하나의 클래스 구조를 변경하면 그와 연관된 클래스를 변경해야 하는 위험이 있습니다.
  • 도메인이나 객체 사용 코드를 변경해야 함으로, 유지보수 측면에서 좋지 않습니다.
class DatabaseHelper {
    fun connect() {
        println("Connected to the database.")
    }

    fun disconnect() {
        println("Disconnected from the database.")
    }

    fun executeQuery(query: String): String {
        println("Executing query: $query")
        return "Results"
    }
}

class UserManager {
    private val dbHelper = DatabaseHelper()

    fun addUser(name: String) {
        dbHelper.connect()
        dbHelper.executeQuery("INSERT INTO users (name) VALUES ('$name')")
        dbHelper.disconnect()
    }

    fun removeUser(name: String) {
        dbHelper.connect()
        dbHelper.executeQuery("DELETE FROM users WHERE name = '$name'")
        dbHelper.disconnect()
    }

    fun getUser(name: String): String {
        dbHelper.connect()
        val result = dbHelper.executeQuery("SELECT * FROM users WHERE name = '$name'")
        dbHelper.disconnect()
        return result
    }
}
  • 위는 결합도가 높은 클래스의 예시입니다.
  • UserManager 클래스는 DatabaseHelper 클래스를 직접 인스턴스화 하고 있으며, 이는 DatabaseHelper 클래스의 변경이 UserManager의 직접적인 영향을 미친다는 것을 의미합니다.
  • 현실세계의 예로 든다면, 핸드폰의 부품을 변경해야 할 때, 핸드폰 기기를 새로 구매한다는 예로 들 수 있습니다.
  • 객체지향의 관점에서 결합도는 클래스 또는 메서드가 협력에 필요한 적절한 수준의 관계만을 유지해야 합니다.
  • 클래스들 간에 연관이 있을때 인터페이스로 제대로 분리되어 있지 않고 불필요하게 많은 정보를 알고 있다면, 이는 결합도가 높게 측정되게 됩니다.
  • 따라서 좋은 소프트웨어는 낮은 결합도를 가지고 있다라고 말할 수 있습니다.

응집도(Cohesion)

  • 응집도는 하나의 클래스가 기능에 집중하기 위한 모든 정보와 역할을 가지고 있어야 한다는 의미입니다.
  • 한 모듈 내의 구성 요소 간의 밀접한 정도를 의미하며, 한 모듈이 하나의 기능에 대한 책임을 가지고 있는 것은 응집도가 높은 것입니다.
  • 만약 한 모듈이 여러 기능을 갖고 있다면, 응집도가 낮다고 할 수 있습니다.
  • 응집도가 높은 모듈은 하나의 모듈 안에 함수나 데이터와 같은 구성 요소들이 하나의 기능을 구현하기 위해 필요한 것들만 배치되어 있으며, 긴밀하게 협력합니다.
  • 반면에 응집도가 낮은 모듈은 모듈 내부에 서로 관련 없는 함수나 데이터들이 존재하거나, 관련성이 적은 여러 기능들이 서로 다른 목적을 추구하며 산재해 있습니다.
  • 응집도가 낮은 예
    • UserService 클래스는 사용자 관리, 이메일 전송, 리포트 생성 등 여러 책임을 수행하고 있습니다.
    • 이 클래스의 메서드들은 서로 밀접하게 관련되지 않은 작업들을 처리합니다.
    • 이는 클래스의 응집도를 낮추고 유지 보수성을 떨어뜨립니다.
class UserService {
    fun addUser(user: User) {
        // Logic to add user to the database
    }

    fun removeUser(userId: String) {
        // Logic to remove user from the database
    }

    fun getUser(userId: String): User? {
        // Logic to retrieve user from the database
    }

    fun sendWelcomeEmail(user: User) {
        // Logic to send welcome email to the user
    }

    fun generateUserReport(userId: String): Report {
        // Logic to generate a report for the user
    }
}
  • 응집도가 높은 예
    • 각 클래스들이 단일 책임에 집중하고 있습니다.
    • 이로써 각 클래스의 응집도가 높아지고, 유지 보수성과 재사용성이 향상되었습니다.
class UserRepository {
    fun addUser(user: User) {
        // Logic to add user to the database
    }

    fun removeUser(userId: String) {
        // Logic to remove user from the database
    }

    fun getUser(userId: String): User? {
        // Logic to retrieve user from the database
    }
}

class EmailService {
    fun sendWelcomeEmail(user: User) {
        // Logic to send welcome email to the user
    }
}

class UserReportService {
    fun generateUserReport(userId: String): Report {
        // Logic to generate a report for the user
    }
}
  • 이처럼 클래스를 분리함으로써 각 클래스가 하나의 책임에 집중하도록 한다면, 코드의 응집도가 높아지고 아래와 같은 장점을 얻을 수 있습니다.
    • 코드의 가독성 향상
    • 유지 보수성 향상
    • 테스트 용이성 향상
  • 응집도가 높은 클래스는 단일 책임을 가진 클래스, 다른 클래스와 잘 협력하는 클래스라고 할 수 있습니다.

참고

https://inpa.tistory.com/entry/OOP-💠-객체의-결합도-응집도-의미와-단계-이해하기-쉽게-정리