SOLID
- 단일 책임 원칙(single responsibility principle)
- 개방-폐쇄 원칙 (Open/closed principle)
- 리스코프 치환 원칙 (Liskov substitution principle)
- 인터페이스 분리 원칙 (Interface segregation principle)
- 의존관계 역전 원칙 (Dependency inversion principle)
SRP
단일 책임 원칙
하나의 클래스는 하나의 책임만 가져야 한다는 원칙이다.
이게 무슨 뜻일까?
책임이라는 의미가 참 모호하다.
그래서 SRP의 중요한 기준을 변경으로 보면 편하다.
변경이 있을 때 다른 코드를 고칠게 적다면 단일 책임 원칙을 잘 따른 것이다.
예를 들어 게시판 기능을 만드는데 게시물 추천 기능이 새로 추가되었을 때
게시물 생성에서부터 모든 코드들을 다 뜯어고쳐야 된다면 SRP가 잘 안지켜진 것이다.
하지만 책임을 너무 작게 잡으면 코드를 너무 많이 나눠야하기 때문에
적절하게 조절하는 것이 필요하다.
OCP
개방-폐쇄 원칙
소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야 한다.
뭔가 모순되는 말처럼 보인다. 확장을 하려면 변경을 해야하는 것 아닌가?
여기서 우리는 다형성에 대해서 생각해야한다.
예를 들어 한 종류의 칫솔이 있다.
우리가 새로운 종류의 칫솔을 산다고해서 양치하는 법을 새로 배워야하는가?
결국 OCP란 다형성이다.
인터페이스를 구현한 새로운 클래스를 만든다는 의미이다.
새로운 클래스를 만드는데에는 기존 코드의 변경이 필요하지 않다.
하지만 그래도 문제점이 존재한다.
만약 우리가 새로운 종류의 칫솔을 쓴다고하면
1
2
3
4
5
6
// ToothBrushService 클라이언트가 구현 클래스를 직접 선택
// 기존 코드
ToothBrush t = new BraunToothBrush();
// 바꿔야 하는 코드
ToothBrush t = new OralbToothBrush();
이런식으로 결국 기존코드를 바꿔야하는 상황이 생길 수 밖에 없다.
그렇다면 이 문제를 해결하려면
객체를 생성하고 연관관계를 맺어주는 별도의 조립, 설정자가 필요하다.
그 역할을 하는 것이 스프링이다!
LSP
리스코프 치환 원칙
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서
하위 타입의 인스턴스로 바꿀 수 있어야 한다.
다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것이다.
컴파일에서 오류가 나는지 성공하는지의 문제가 아니다.
ISP
인터페이스 분리 원칙
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
예를 들어 자동차 인터페이스를 운전 인터페이스와 정비 인터페이스로 나누고
사용자 클라이언트를 운전자 인터페이스와 정비사 인터페이스로 나누면
정비 인터페이스가 변하더라도 운전자 클라이언트에 영향을 주지 않는다.
이렇게 되면 인터페이스가 명확해지고 대체 가능성이 높아지게 된다.
DIP
의존관계 역전 원칙
프로그래머는 추상화에 의존해야지 구체화에 의존하면 안된다.
이게 무슨 소리일까?
쉽게 이야기하면 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻이다.
예를 들어 사용자는 칫솔의 역할에 대해 집중해야지
특정 칫솔에 집중하면 안된다는 것이다.
칫솔의 역할에 대해 집중해야 칫솔이 바뀌더라도 유연하게 구현체를 변경할 수 있다.
구현체에 의존하게 되면 변경이 아주 어려워진다.
하지만 여기서도 문제가 생기게 된다.
아까 예를 들었던 코드를 다시 보자
1
2
// ToothBrushService 클라이언트가 구현 클래스를 직접 선택
ToothBrush t = new BraunToothBrush();
이 코드에서 ToothBrushService는 인터페이스에 의존하지만 구현 클래스도 동시에 의존한다.
의존한다는 것은 아는 것이다.
위의 코드에서 BraunToothBrush()를 알고 있어야 동작이 가능하다 그러므로 구현 클래스에 의존하는 것이다.
이것은 결국 DIP를 위반한 것이다.
이것 또한 해결해줄 수 있는 것이 Spring인 것이다.
결론
객체지향의 핵심은 다형성이다.
하지만 다형성만으로는 SOLID를 만족하며
쉽게 부품을 갈아끼우듯이 코드를 바꿀 수가 없다.
왜냐하면 구현 객체를 변경할 때 클라이언트 코드도 함께 변경되기 때문이다.
그렇기 때문에 그 역할을 대신 해줄 스프링같은 프레임워크가 필요한 것이다.