Computer >> 컴퓨터 >  >> 프로그래밍 >> C#

C# 인터페이스 분리 원칙(ISP)이란? 개념부터 구현 예제까지

"클라이언트는 자신이 사용하지 않는 인터페이스에 의존하도록 강요받아서는 안 된다."

인터페이스 분리 원칙(Interface Segregation Principle, ISP)은 SOLID 설계 원칙 중 하나로, 클라이언트가 실제로 사용하지 않는 메서드나 속성까지 구현하도록 강제하지 말아야 한다는 원칙입니다.

쉽게 말해, 여러 기능이 뒤섞인 하나의 거대한 '뚱뚱한(fat) 인터페이스'를 만들기보다는, 메서드의 성격별로 작은 인터페이스 여러 개로 나누고, 각 인터페이스가 하나의 하위 모듈만 담당하도록 설계하는 것이 바람직하다는 의미입니다.

인터페이스 분리 전의 문제점

아래 예제는 제품 정보를 표현하는 IProduct 인터페이스에 모든 속성을 몰아넣은 경우입니다. 바지와 모자라는 서로 다른 제품이 같은 인터페이스를 강제로 구현하고 있어 문제가 발생합니다.

public interface IProduct {
    int ID { get; set; }
    double Weight { get; set; }
    int Stock { get; set; }
    int Inseam { get; set; }      // 바지 길이
    int WaistSize { get; set; }   // 허리 사이즈
}

public class Jeans : IProduct {
    public int ID { get; set; }
    public double Weight { get; set; }
    public int Stock { get; set; }
    public int Inseam { get; set; }
    public int WaistSize { get; set; }
}

public class BaseballCap : IProduct {
    public int ID { get; set; }
    public double Weight { get; set; }
    public int Stock { get; set; }
    // 야구 모자에는 필요 없는 속성까지 억지로 구현해야 함
    public int Inseam { get; set; }
    public int WaistSize { get; set; }
    public int HatSize { get; set; }
}

BaseballCap 클래스를 보면 Inseam(바지 기장)과 WaistSize(허리 사이즈)는 모자와 전혀 관련이 없음에도 불구하고, 인터페이스 구현 의무 때문에 어쩔 수 없이 작성해야 합니다. 이처럼 불필요한 의존성은 코드의 가독성과 유지보수성을 크게 떨어뜨립니다.

인터페이스 분리 적용 후

ISP를 적용하면 공통 속성은 IProduct에 두고, 제품 유형별 특성은 별도의 인터페이스로 분리할 수 있습니다.

public interface IProduct {
    int ID { get; set; }
    double Weight { get; set; }
    int Stock { get; set; }
}

public interface IPants {
    int Inseam { get; set; }
    int WaistSize { get; set; }
}

public interface IHat {
    int HatSize { get; set; }
}

public class Jeans : IProduct, IPants {
    public int ID { get; set; }
    public double Weight { get; set; }
    public int Stock { get; set; }
    public int Inseam { get; set; }
    public int WaistSize { get; set; }
}

public class BaseballCap : IProduct, IHat {
    public int ID { get; set; }
    public double Weight { get; set; }
    public int Stock { get; set; }
    public int HatSize { get; set; }
}

이제 Jeans 클래스는 IProductIPants를, BaseballCap 클래스는 IProductIHat를 각각 구현합니다. 덕분에 각 클래스는 자신에게 실제로 필요한 멤버만 포함하게 되며, 다음과 같은 이점을 얻습니다.

  • 낮은 결합도: 클래스가 불필요한 멤버에 의존하지 않습니다.
  • 높은 응집도: 각 인터페이스가 명확한 단일 역할을 수행합니다.
  • 유연한 확장성: 새로운 제품 유형이 추가되어도 기존 코드에 영향을 주지 않고 필요한 인터페이스만 조합하면 됩니다.
  • 손쉬운 테스트: 작은 단위의 인터페이스는 목(Mock) 객체 생성과 단위 테스트가 훨씬 간편합니다.

정리

인터페이스 분리 원칙은 "하나의 범용 인터페이스보다 여러 개의 구체적인 인터페이스가 낫다"는 설계 철학입니다. C#에서는 인터페이스를 역할별로 잘게 쪼개고, 클래스가 필요한 인터페이스만 선택적으로 구현하도록 함으로써 유지보수하기 좋고 확장 가능한 코드를 만들 수 있습니다.