0. SOLID란?
- 로버트 마틴(클린코드 저자로 유명)이 좋은 객체 지향 설계의 5가지 원칙을 정리함
- SRP: 단일 책임 원칙(single responsibility principle)
- OCP: 개방-폐쇄 원칙(open/closed principle)
- LSP: 리스코프 치환 원칙(liskov substitution principle)
- ISP: 인터페이스 분리 원칙(interface segregation principle)
- DIP: 의존관계 역전 원칙(dependency inversion principle)
1. SRP 단일 책임 원칙
(Single Responsibility Principle)
- 하나의 클래스는 하나의 책임만 가져야 한다.
- 하나의 책임이라고 하면, 기준이 모호해 질 수 있다.
-> 사람에 따라 책임이 클수도, 작을 수도 있다.
-> 상황, 경우에 따라 다르다
- 그럼 어떻게 판단하느냐???
-> "변경"이 중요한 기준이다.
-> 변경이 있을 때 파급 효과가 적다면, 단일 책임 원칙을 잘 지킨 것
ex) 사소한 UI 하나를 변경하는데, 여러 파일들을 다 고쳐야 한다?
-> 잘못 설계된 것
- 결국 클래스 책임 범위를 너무 작게 잡으면, 파일이 많이 생성됨
- 너무 크게 잡으면, 단일 책임 원칙을 지킬 수 없게 됨
-> 적절하게 조절하는 것이 개발자의 경험에 의한 것
2. OCP 개방-폐쇄 원칙
(Open/Closed Principle)
- 가장 중요한 원칙
- 소프트웨어 요소들은 확장은 열려 있으나, 변경에는 닫혀 있어야 함
-> 이게 무슨 소리일까??
-> 코드를 변경 안하고 기능을 추가한다고?
- 이전 게시글에서 적었던 "다형성"을 생각해보자.
- 다형성을 활용한다면, OCP를 지키면서 코드를 확장할 수 있다.
-> 기존의 인터페이스를 구현한 새로운 클래스를 만들어서, 새로운 기능을 구현한다면?
-> 새로운 클래스, 즉 코드를 "추가"한 것이므로 확장을 한거지, 변경한 것은 X
public class MemberService {
private MemberRepository memberRepository = new MemoryMemberRepository();
}
public class MemberService {
// private MemberRepository memberRepository = new MemoryMemberRepository();
private MemberRepository memberRepository = new JdbcMemberRepository();
}
<이전 게시글에서 다향성의 예시로 들은 코드>
- 어? 근데 위 코드를 보면 구현 객체를 변경하기 위해 클라이언트 코드를 변경했다.
-> 다형성을 사용했지만 OCP 원칙을 지킬 수 없다.. (결국 클라이언트 코드를 수정했지 때문)
- 따라서 객체를 생성하고, 연관관계 맺어주는 설정자가 필요하다.
3. LSP 리스코프 치환 원칙
(Liskov Substitution Principle)
- 리스코프 치환 원칙은 어떻게 보면 오해하기 쉬운 원칙임
- "B가 A의 자식 타입이면 부모 타입인 A객체는 자식 타입인 B로 치환해도, 작동에 문제가 없어야 함"
- 즉, A와 B 부모 자식에 대한 정의가 논리적으로 확실해야, 자식 클래스인 B로 치환해도 문제가 없다는 내용이다.
- 이를 더 자세히 풀어보면,
-> 부모 클래스 A를 타입으로 하고, 자식 클래스 B를 대입시킬 경우에도 문제없이 작동하기 위해서(다형성) 자식클래스는 부모클래스가 따르던 논리적 조건(계약 사항)을 따라야 한다.
(계약 사항: 클래스의 멤버가 어떻게 작동하는 지 대한 구현 조건 사항 등 의미)
- 즉, 최소한 자식 클래스 B는 자신의 부모 클래스 A에서 가능한 모든 행위를 수행할 수 있어야 한다.
ex) 동물이라는 부모 클래스를 인간이라는 자식 클래스로 상속받는 것은 가능하다.
-> 인간은 동물의 특징을 모두 수행할 수 있다.
ex) 직사각형이라는 부모 클래스를 정사각형이라는 자식 클래스로 상속받는 것은 LSP를 위배한다.
-> 정사각형은 직사각형의 특징을 모두 수행할 수 없다.
-> 반례) 직사각형은 세로의 길이만 20 늘릴 수 있지만, 정사각형은 불가능하다.
(정사각형은 가로와 세로의 길이가 무조건 같아야 하므로)
4. ISP 인터페이스 분리 원칙
(Interface segregation principle)
- 특정 클라이언트를 위한 여러개의 인터페이스가, 범용 인터페이스 하나보다는 낫다는 원칙
ex) 설계를
1. 자동차 인터페이스 / 사용자 클라이언트
2. 운전 인터페이스, 정비 인터페이스 / 운전자 클라이언트, 정비사 클라이언트
- 1번과 다르게 2번의 경우는 기능을 분리함, 이때의 이점은?
-> 정비 인터페이스를 변경해도, 운전자 클라이언트에 영향을 주지 않음
-> 인터페이스 각각이 명확해지며, 대체 가능성이 높아짐
5. DIP 의존관계 역전 원칙
(Dependency inversion principle)
- DIP는 "추상화에 의존하고, 구체화에 의존하면 안된다"라는 내용을 따르는 방법
- 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻
- 즉, 클라이언트가 인터페이스에 의존해야지만, 구현체를 유연하게 변경할 수 있게 됨
-> 구현체에 의존한다면? 변경이 어려워진다.
- 그런데 OCP에서 설명했던 MemberService예시를 다시 보면
- 인터페이스에 의존하지만, 동시에 구현 클래스도 의존한다.
-> Why? MemberService 클라이언트가 구현 클래스를 직접 선택했기 때문이다.
ex) MemberRepository m = new MemoryMemberRepository();
- 이는 DIP를 위반하는 행위이다.
6. 그럼 어떻게 해야함?
- 객체 지향의 핵심은 다형성이고, SOLID 원칙도 알게 되었다.
- 하지만 다형성 만으로는, 부품을 갈아 끼우는 것처럼 코딩하기는 힘들다
(구현 객체를 변경할 때, 클라이언트 코드도 변경해야 함)
- 즉, 다형성 만으로는 OCP, DIP를 지킬 수 없다는 것이 결론
- 뭔가 다른 요소가 필요하다....
위 내용은 김영한 님의 인프런 강의 "스프링 핵심 원리 - 기본편"의 내용과 강의자료를 토대로 작성된 게시글입니다.
강의 링크:
'Web > spring study' 카테고리의 다른 글
[Spring] 예시 스프링 프로젝트 설계 (0) | 2023.01.09 |
---|---|
[Spring] IOC, DI, 컨테이너 (0) | 2022.12.17 |
[Spring] 객체 지향 설계와 스프링 (0) | 2022.05.26 |
[Spring] 좋은 객체지향 프로그래밍이란? (0) | 2022.05.17 |
[Spring] 스프링이란? (0) | 2022.04.19 |
댓글