인터페이스 분리 원칙(ISP)

인터페이스 분리 원칙이란?

인터페이스 분리 원칙Interface Segregation Principle, ISP은 SOLID 원칙 중 네 번째 원칙으로, 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않도록 인터페이스를 분리해야 한다는 개념을 제시합니다. 즉, 하나의 크고 복잡한 인터페이스를 여러 개의 작은 인터페이스로 나누어, 각 인터페이스가 특정한 역할만을 수행하도록 설계하는 것입니다.

ISP의 목적과 중요성

불필요한 의존성 제거

인터페이스가 너무 많은 메서드를 포함하면, 클라이언트는 자신이 필요하지 않은 기능에도 의존하게 됩니다. 이는 코드의 복잡성을 증가시키고, 유지보수를 어렵게 만듭니다. ISP는 이러한 문제를 방지하기 위해 인터페이스를 세분화하여 클라이언트가 자신이 사용하는 메서드에만 의존할 수 있도록 합니다.

코드의 유연성 향상

작고 명확한 인터페이스를 사용하면, 시스템이 변화하거나 확장될 때 기존 코드를 수정하지 않고도 새로운 기능을 추가할 수 있습니다. 이는 코드의 유연성과 확장성을 높여줍니다.

변경의 파급 효과 최소화

인터페이스가 잘 설계되면, 인터페이스에 변경이 생기더라도 그 인터페이스를 구현한 클래스들이 불필요한 영향을 받지 않습니다. 이를 통해, 시스템 내의 변화가 최소한의 영향만을 미치게 되어 안정성이 증가합니다.

ISP의 적용 예시

ISP를 위반한 사례

아래 예제에서는 ISP를 위반한 인터페이스가 등장합니다.

public interface IWorker
{
    void Work();
    void Eat();
}

이 인터페이스를 구현한 클래스는 다음과 같습니다:

public class Worker : IWorker
{
    public void Work() => Console.WriteLine("Working...");
    public void Eat() => Console.WriteLine("Eating...");
}
public class Robot : IWorker
{
    public void Work() => Console.WriteLine("Robot working...");
	// 로봇은 먹지 않으므로 이 메서드는 필요 없음
    public void Eat() => throw new NotImplementedException();
}
  • Robot 클래스는 IWorker 인터페이스를 구현하는 과정에서 Eat 메서드를 사용하지 않지만, 이 메서드를 구현해야만 합니다.
  • 이는 인터페이스가 지나치게 많은 책임을 가지게 되면서 발생하는 문제입니다.

ISP를 준수한 사례

ISP를 적용하여 인터페이스를 분리할 수 있습니다.

public interface IWorkable
{
    void Work();
}
public interface IFeedable
{
    void Eat();
}
public class Worker : IWorkable, IFeedable
{
    public void Work() => Console.WriteLine("Working...");
    public void Eat() => Console.WriteLine("Eating...");
}
public class Robot : IWorkable
{
    public void Work() => Console.WriteLine("Robot working...");
}
  • 인터페이스를 IWorkableIFeedable로 분리하여, Worker 클래스는 두 인터페이스를 모두 구현하지만, Robot 클래스는 IWorkable 인터페이스만 구현하도록 변경되었습니다.
  • 이로써 Robot 클래스는 자신이 사용하지 않는 Eat 메서드를 구현할 필요가 없어졌습니다.

ISP와 객체지향 4대 원칙과의 관계

ISP 와 캡슐화

인터페이스를 분리하여 각 인터페이스가 특정한 책임을 가지도록 하면, 각 클래스는 자신의 상태와 동작을 더 효과적으로 캡슐화할 수 있습니다. 이는 클래스가 외부에서 불필요한 접근을 차단하고, 자신의 핵심 역할에 집중하도록 도와줍니다.

ISP 와 상속

인터페이스 분리 원칙을 적용하면 상속 구조가 더 명확해집니다. 각 클래스는 자신이 필요한 인터페이스만을 구현하고, 불필요한 인터페이스는 상속받지 않으므로, 상속 구조가 복잡해지지 않으며 유지보수가 용이해집니다.

ISP 와 다형성

다형성은 다양한 클래스가 동일한 인터페이스를 구현함으로써, 동일한 메서드 호출에 대해 다양한 행동을 수행할 수 있게 합니다. 인터페이스를 잘 분리하면, 다형성을 보다 효과적으로 활용할 수 있으며, 클라이언트는 필요에 따라 적합한 인터페이스를 선택하여 구현 클래스를 사용할 수 있습니다.

ISP 와 추상화

추상화는 시스템의 복잡성을 줄이기 위해 필수적인 요소만을 노출하는 것을 의미합니다. ISP를 통해 인터페이스를 세분화함으로써, 각 클래스는 필요한 추상화만을 구현하게 됩니다. 이는 불필요한 메서드가 포함되지 않도록 하여, 코드의 가독성과 유지보수성을 높입니다.

ISP와 SOLID 원칙과의 연계

ISP 와 단일 책임 원칙

단일 책임 원칙SRP을 적용하여 클래스를 설계할 때, 각 클래스가 하나의 책임만을 가질 수 있도록 인터페이스를 분리하는 것이 중요합니다. 이는 클래스가 불필요한 기능에 의존하지 않도록 하여, 시스템의 복잡성을 줄이고 유지보수를 용이하게 만듭니다.

ISP 와 개방_폐쇄 원칙

ISP는 개방 폐쇄 원칙OCP를 지원합니다. 작은 인터페이스로 분리된 클래스들은 새로운 기능을 추가할 때 기존 코드를 수정하지 않고도 확장이 가능하게 됩니다. 이는 코드의 안정성을 높이고, 시스템을 변화에 유연하게 대응할 수 있게 합니다.

ISP 와 의존 역전 원칙

ISP는 의존 역전 원칙DIP을 구현하는 데 중요한 역할을 합니다. 세분화된 인터페이스는 고수준 모듈이 저수준 모듈에 의존하지 않도록 하고, 추상화된 인터페이스에 의존할 수 있게 합니다. 이는 시스템의 모듈성을 높이고, 의존성 주입을 통한 유연한 설계를 가능하게 합니다.

ISP의 한계

복잡성 증가

인터페이스를 너무 세분화하면 오히려 복잡성이 증가할 수 있습니다. 너무 많은 인터페이스를 정의하면 클래스 간의 관계가 복잡해지고, 코드의 가독성이 떨어질 수 있습니다. 적절한 수준에서 인터페이스를 분리하는 것이 중요합니다.

관리의 어려움

세분화된 인터페이스가 많아지면, 각각의 인터페이스와 그 구현체들을 관리하는 것이 어려워질 수 있습니다. 이는 특히 대규모 시스템에서 유지보수의 부담을 증가시킬 수 있습니다.

맺음말

인터페이스 분리 원칙ISP은 시스템의 복잡성을 줄이고, 코드의 유연성과 재사용성을 높이는 데 중요한 역할을 합니다. 이를 통해 클래스는 자신이 사용하는 기능에만 집중할 수 있으며, 불필요한 의존성을 제거하여 코드의 품질을 향상시킬 수 있습니다. 하지만, 인터페이스를 지나치게 세분화하면 오히려 복잡성이 증가할 수 있으므로, 실무에서는 적절한 수준에서 ISP를 적용하는 것이 중요합니다.