SOLID란?

- 디자인 설계에서 고려되야 하는 기본 원칙
- SRP(Single Responsibility Principle)
- OCP(Open Closed Principle)
- LSP(Liskov Substiution Principle)
- ISP(Interface Segregation Principle)
- DIP(Dependency Inversion Principle)
SRP
- 각 클래스가 오직 하나의 책임을 져야함
- 하나의 클래스가 여러 책임을 지게 되면 코드를 이해하기 난해해짐
- 클래스의 책임이 명확해져 유지보수 비용이 줄어듦
OCP
- 확장성을 염두해둔 코드 설계
- 만약 외부의 요구 사항이 변경되어도 기존 코드의 변경 없이 대응이 가능하도록 설계
- 기존의 코드는 Closed가 되어서 안정성 증가
func eat(_ apple: Apple) {
// Closed된 코드
}
- 여기서 Apple은 Open된 외부의 요구사항이고, eat함수 안의 코드는 Closed되어 있음
LSP
- 인터페이스(Swift에서는 프로토콜)를 통해서 설계해야함
- 하위 모듈이 변경되어도 동작이 가능함
ISP
- public으로 노출되는 부분을 프로토콜로 추상화 해야함
DIP
- 구현된 모듈이 프로토콜에 의존
- 그 반대는 성립X
- 프로토콜에만 의존하여 결합도를 낮출 수 있음
LSP, ISP, DIP는 모두 프로토콜과 연관되어 있어서 한꺼번에 예시로 설명
class Apple: Fruit {
func cost() {}
}
class Banana: Fruit {
func cost() {}
}
class Melon: Fruit {
func cost() {}
}
- 만약 과일이라는 공통적인 특성을 가지는 Apple, Banana, Melon이 있음
- 이 과일들은 모두 cost라는 공통적인 메서드를 가짐
// ISP
protocol Fruit {
func cost()
}
- 공통적인 프로토콜(인터페이스)로 추상화한 코드
- 이것은 앞서 언급했던 ISP에 입각된 것
- Fruit의 하위 모듈인 Apple, Banana, Melon이 직접 구현하게 됨
func pay(fruit: Fruit)
let apple = Apple()
pay(fruit: apple) // DIP
- 위 pay라는 함수는 추상화된 프로토콜을 인자로 받고 있음
- 추상화된 프로토콜을 바라보면서 구체적인 하위 모듈을 알 필요가 없음
- 이 과정이 DIP에 입각된 것, 이것이 유명한 의존성 주입(DI)
let banana = Banana()
pay(fruit: banana) // LSP
- 만약 하위 모듈이 바뀌더라도 pay라는 함수는 여전히 바뀌지 않음
- 어떤 과일이 오든간에 추가 구현이 필요 없는 확장성을 설계 및 구현할 수 있음
- 만약 아예 새로운 과일이 추가 되었다 하더라도 상관 없음
- 이 과정이 LSP에 입각된 것
Solid의 목표
- 코드들의 응집도를 높이고
- 결합도는 낮춰야함
- 결국 확장성을 넓히고 유지 보수 비용을 최소화 하기 위함