1. SOLID 원칙

더보기

1. SOLID

 

OOP, 객체 지향 프로그래밍 설계의 기본 원칙

 

SOLID  Eng. Kor.
SRP Single Responsibility Principle 단일 책임 원칙
OCP Open-Closed Principle 개방-폐쇄 원칙 
LSP Liskov Substitution Principle 리스코프 치환 원칙 
ISP Interface Segregation Principle 인터페이스 분리 원칙
DIP Dependency Inversion Principle 의존성 역전 원칙

 

2. 인터페이스 분리 원칙(ISP, Interface Segregation Principle)

더보기
Many client-specific interfaces are better than one general-purpose interface

 

"구현할 필요가 없는 기능을 가진, 인터페이스에 의존하도록 강요받으면 안 된다."
→ 즉, 클라이언트는 자신이 필요로 하는 메서드만 가진 작은 인터페이스에 의존해야 한다.

 

 

ISP의 주요 목표

  1. 작고 구체적인 인터페이스를 정의하여 , 사용하지 않는 메서드를 구현하지 않도록 한다.
  2. 인터페이스를 필요에 따라 분리 → 단일 책임 원칙(SRP) 강화

 

 

3. 참새, 팽귄 + 오리 예시

더보기

...이전 포스트의 LSP 예제를 이어 사용합니다.

 

1. 가정 (ISP 위반 사례)

  • 하나의 커다란 인터페이스에서 여러 기능을 정의하고, 모든 클래스가 이를 강제 구현
  • 클라이언트가 필요하지 않은 메서드까지 구현해야 하는 경우 발생

 

 

 

2. ISP를 위반한 코드

using System;
namespace ISPViolationExample
{
// ❌ 너무 많은 책임을 가지는 인터페이스 (ISP 위반)
public interface IBird
{
void Eat();
void Fly();
void Swim();
}
// ❌ 참새는 수영할 수 없지만 인터페이스의 Swim()을 구현해야 함 → ISP 위반
public class Sparrow : IBird
{
public void Eat()
{
Console.WriteLine("참새가 먹이를 먹는다.");
}
public void Fly()
{
Console.WriteLine("참새가 날아간다.");
}
public void Swim()
{
Console.WriteLine("참새는 수영할 수 없다."); // ❌ 잘못된 구현 → ISP 위반
}
}
// ❌ 펭귄은 날 수 없지만 인터페이스의 Fly()를 구현해야 함 → ISP 위반
public class Penguin : IBird
{
public void Eat()
{
Console.WriteLine("펭귄이 먹이를 먹는다.");
}
public void Fly()
{
throw new NotImplementedException("펭귄은 날 수 없습니다."); // ❌ 강제 구현 → ISP 위반
}
public void Swim()
{
Console.WriteLine("펭귄이 수영한다.");
}
}
class Program
{
static void Main()
{
IBird sparrow = new Sparrow();
sparrow.Eat();
sparrow.Fly(); // 정상
sparrow.Swim(); // ❌ 의미 없는 동작 수행
IBird penguin = new Penguin();
penguin.Eat();
penguin.Fly(); // ❌ 런타임 오류 발생 가능 → ISP 위반
penguin.Swim(); // 정상
}
}
}

 

 

 

 

3. ❌ OCP를 위반한 코드 문제점 파악

  • IBird 인터페이스에 너무 많은 기능 포함 → 클라이언트가 필요 없는 메서드를 구현해야 함
  • 펭귄은 날 수 없음에도 Fly()를 구현해야 함 → ISP 위반
  • 참새는 수영할 수 없음에도 Swim()을 구현해야 함 → ISP 위반

 

 

 

 

4.  OCP가 지켜진 코드

using System;
namespace ISPExample
{
// ✅ 기본 Bird 인터페이스 정의 (공통 기능만 정의)
public interface IBird
{
void Eat();
}
// ✅ 날 수 있는 새 인터페이스 정의
public interface IFlyable
{
void Fly();
}
// ✅ 수영할 수 있는 새 인터페이스 정의
public interface ISwimmable
{
void Swim();
}
// ✅ 참새는 날 수 있는 새이므로 IFlyable 구현
public class Sparrow : IBird, IFlyable
{
public void Eat()
{
Console.WriteLine("참새가 먹이를 먹는다.");
}
public void Fly()
{
Console.WriteLine("참새가 날아간다.");
}
}
// ✅ 펭귄은 수영할 수 있는 새이므로 ISwimmable 구현
public class Penguin : IBird, ISwimmable
{
public void Eat()
{
Console.WriteLine("펭귄이 먹이를 먹는다.");
}
public void Swim()
{
Console.WriteLine("펭귄이 수영한다.");
}
}
// ✅ 오리는 날고 수영할 수 있으므로 두 인터페이스 모두 구현
public class Duck : IBird, IFlyable, ISwimmable
{
public void Eat()
{
Console.WriteLine("오리가 먹이를 먹는다.");
}
public void Fly()
{
Console.WriteLine("오리가 날아간다.");
}
public void Swim()
{
Console.WriteLine("오리가 수영한다.");
}
}
class Program
{
static void Main()
{
IBird sparrow = new Sparrow();
sparrow.Eat();
((IFlyable)sparrow).Fly(); // 참새는 날 수 있음
IBird penguin = new Penguin();
penguin.Eat();
((ISwimmable)penguin).Swim(); // 펭귄은 수영할 수 있음
IBird duck = new Duck();
duck.Eat();
((IFlyable)duck).Fly(); // 오리는 날 수 있음
((ISwimmable)duck).Swim(); // 오리는 수영할 수 있음
}
}
}

 

 

5.  ✅ ISP를 준수한 코드에서 개선된 부분 파악

 

  • 필요한 기능별로 인터페이스를 분리
  • 날 수 있는 새는 IFlyable 인터페이스에서 정의
  • 수영할 수 있는 새는 ISwimmable 인터페이스에서 정의
  • 인터페이스를 명확하게 구분함으로써 불필요한 메서드 구현 방지

 

 

4. 과제

더보기

ISP를 준수하여, 수륙양용차를 C#으로 구현하세요.

  1. Car 클래스 → 육지에서만 동작
  2. Boat 클래스 → 물에서만 동작
  3. AmphibiousCar 클래스 → 육지와 물에서 모두 동작

 

참고 링크(Python 코드임)

https://youtu.be/WBJm4U86m5k?si=ulAkj83ULUxLDUSp