안녕하세요. 도미닉입니다.
오늘은 SOLID 에 대해 정리해보겠습니다.
SOLID 는 5가지 원칙을 가지고 있습니다.
하나씩 예시를 들어가며 설명해드리겠습니다.
1. SRP (Single Responsibility Principle)
첫번째는 단일 책임 원칙입니다.
작성한 클래스는 하나의 기능과 책임을 가져야만 한다는 원칙입니다.
아래와 같이 기타 구조체를 생성했다면 SRP 원칙에 위반되는 것입니다.
왜냐하면 price 부터 maker, type, model, backWood, topWood, stringNum 은 기타의 모델과 상태에 따라 달라질 수 있기 때문입니다.
수정이 일어날 때마다 Guitar 클래스를 변경해주어야하므로 안정적이지 않습니다.
아래와 같이 SRP 원칙을 적용할 수 있습니다.
기타의 변동 가능한 부분이 따로 GuitarSpec 이라는 구조체에 저장이 되었습니다. 변경이 발생하면 GuitarSpec 만 변경하면 됩니다.
보기에도 좋고 변경이 가능한 부분을 한곳에서 관리할 수 있습니다.
2. OCP(Open Close Principle)
두번째는 개방 폐쇄의 원칙입니다.
소프트웨어의 구성요소는 확장에는 열려있고 변경에는 닫혀있어야한다는 원리입니다.
확장은 쉽게 할 수 있고 변경은 최소화할 수 있어야 한다는 뜻이겠습니다.
만약 위에 Guitar 와 GuitarSpec 구조체에 이어서 Violin 과 여러 현악기가 추가되어야 한다면 매번 악기 이름의 구조체와 악기 스팩을 갖는 구조체를 추가해주어야 할까요?
이러한 상황이라면 OCP 원칙을 고려해볼 수 있습니다.
위와 같이 현악기 클래스를 선언했습니다.
이제 기타, 바이올린 등 어떤 현악기를 추가할 때든 StringInstrument 와 StringInstrumentSpec 을 상속받아서 간단히 추가할 수 있습니다.
코드의 수정을 최소화하여 결합도는 줄이고 응집도는 높이는 효과를 볼 수 있습니다.
3. LSP(The Liskov Substitution Principle)
세번째는 리스코브 치환의 원칙입니다.
“서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다.”라고 할 수 있습니다.
서브 타입은 기반 타입이 약속한 규약(public 인터페이스, 물론 메소드가 던지는 예외까지 포함됩니다.)을 지켜야 합니다.
현악기 클래스에 play 라는 메서드를 추가해주었습니다.
또한 기타와 바이올린에 play 라는 메서드를 오버라이딩 해주었습니다.
결과를 보면 현악기 클래스 변수에 서브 타입인 바이올린과 기타가 자유롭게 들어갈 수 있고
기반 타입인 “안알려진 현악기” 또한 자유롭게 들어갈 수 있습니다.
또한 상위 클래스에서 선언한 play 메서드 또한 하위 클래스에서 호출할 수 있습니다.
이렇게 서브 타입이 기반 타입으로 자유롭게교체될 수 있는 것이 LSP 원리입니다.
4. ISP(Interface Segregation Principle)
네번째는 인터페이스 분리의 원칙입니다.
한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다는 원리입니다.
인터페이스를 나눌 수 있으면 나누고 필요없는 인터페이스는 제거해야합니다.
“칠 수 있는” 이라는 프로토콜을 선언했습니다.
현악기는 칠 수 있는 프로토콜을 채택했고 손으로 연주하는 메서드와 도구를 이용해서 연주하는 메서드를 선언해줬습니다.
구현은 제대로 되었지만 바이올린은 도구를 이용해서만 연주할 수 있는데 손으로도 칠 수 있는 등 완전하지 않게 동작합니다.
이제 ISP 원칙에 따르도록 수정해보겠습니다.
두개의 프로토콜로 나눠주었습니다.
5. DIP(Dependency Inversion Principle)
다섯번째는 의존성 역전의 원칙입니다.
하위 클래스는 당연히 상위 클래스에 의존해야합니다.
하지만 하위 클래스의 모듈의 변경이나 기능에 상위 클래스가 의존성을 갖게 되는 현상을 의존성 역전이라고 합니다.
예를 들어 조금 전의 예제 소스는 기타만 손으로 칠 수 있는 상황이었는데
현악기가 Playable 프로토콜을 채택함에 따라 현악기를 상속받는 모든 악기는 playWithHands 메서드를 사용할 수 있었습니다.
DIP 원칙에 따라 현악기에는 도구로 칠 수 있다는 프로토콜만 채택했습니다.
아래와 같이 기타는 손으로도 칠 수 있기 때문에 “손으로 칠 수 있다”는 프로토콜도 채택하였습니다.
이제 현악기 타입의 클래스 변수에서 playWithHands 는 오류가 발생합니다.
기타 타입으로 선언해야만 playWithHands 를 호출할 수 있기 때문에 조금 더 안정적이며 명확해졌습니다.