참고
Spring core basic 시리즈는 김영한 님의 "스프링 핵심 원리 - 기본편" 강의를 정리한 글입니다. 글에 첨부된 사진은 해당 강의의 강의 자료에서 캡쳐한 것입니다. 제 Github에만 올려뒀다가, 정보 공유와 강의 홍보(?)를 위해 블로그에도 업로드합니다. 마크다운을 잘 쓰지 못해서 가독성이 조금 떨어지는 점 양해 바랍니다.
스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com
SOLID - 좋은 객체 지향 설계의 5가지 원칙
- SRP: 단일 책임 원칙 (Single Responsibility Principle)
- OCP: 개방-폐쇄 원칙 (Open/Closed Principle)
- LSP: 리스코프 치환 원칙 (Liskov Substitution Principle)
- ISP: 인터페이스 분리 원칙 (Interface Segregation Principle)
- DIP: 의존관계 역전 원칙 (Dependency Inversion Principle)
단일 책임 원칙 SRP (Single Responsibility Principle)
하나의 클래스는 하나의 책임만 가져야 한다.
하나의 책임이라는 것은 모호하다.
- 책임이 클 수도 있고, 아주 작을 수도 있다.
- 문맥과 상황에 따라 달라진다.
중요한 판단의 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 SRP를 잘 따른 것이다.
- ex) UI 변경, 객체의 생성과 사용을 분리
책임(기능)을 과도하게 쪼개면 클래스가 너무 많아지고, 그렇다고 책임을 몰아주면 SRP가 깨지기 때문에, 적절하게 조절하며 설계하는 것이 중요하다.
개방-폐쇄 원칙 OCP (Open/Closed Principle)
소프트웨어 요소는 확장에는 열려 있으나 (Open), 변경에는 닫혀 있어야 한다 (Close).
확장을 하려면 코드를 변경해야만 하는 것 아닌가? -> NO!
다형성을 잘 활용하면 된다! 인터페이스를 구현한 새로운 클래스를 통해 새로운 기능을 구현함으로써, 기존 코드를 변경하지 않고 확장이 가능하다!
OCP를 구현할 때 발생하는 문제점
public class MemberService
{
//둘 다 가능하다!
//private MemberRepository mr = new MemoryMemberRepository();
private MemberRepository mr = new JDBCMemberRepository();
}
MemberService 클라이언트가 구현 클래스를 직접 선택하는 상황에서, 구현 객체를 변경하려면 클라이언트 코드를 변경해야 한다! 분명 다형성을 활용해서 인터페이스와 구현체를 구분했지만, OCP를 지킬 수 없다...?
객체를 생성하고 연관 관계를 맺어주는 별도의 조립, 설정자가 필요하다!
-> 이 부분을 Spring container가 해준다. DI, IoC 등...
리스코프 치환 원칙 LSP (Liskov Substitution Principle)
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다. 단순히 컴파일에 성공하는 것을 넘어서는 내용이다.
다형성의 관점에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것. 다형성을 의미상으로도 완벽하게 지원하기 위한 원칙이다. 인터페이스를 구현한 모든 구현체를 믿고 사용하려면 이 원칙을 지켜야만 한다!
예를 들어, 자동차 인터페이스의 "엑셀" 기능은 자동차를 앞으로 가게 하는 기능인데, 이것을 뒤로 가게 구현한 경우는 LSP를 위반한 것이다. 엑셀 기능을 구현했기 때문에 컴파일 오류는 나지 않지만, 이런 구현체가 존재할 수 있다면 구현체를 믿고 사용할 수가 없어진다!
인터페이스 분리 원칙 ISP (Interface Segregation Principle)
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 더 낫다.
예를 들어 "자동차" 인터페이스가 있다고 하자. 여기에는 운전에 대한 기능, 정비에 대한 기능 등이 포함되어 있을 것이다.
이 자동차 인터페이스를 "운전" 인터페이스와 "정비" 인터페이스 두개로 분리한다!
그러면 "사용자" 클라이언트를 "운전자" 클라이언트와 "정비사" 클라이언트로 분리할 수 있다. 자동차 인터페이스를 분리하는 것을 통해서, 정비 인터페이스가 변해도 운전자 클라이언트에 영향을 주지 않게 만들 수 있다.
따라서 인터페이스가 명확해지고 (기능이 줄어드니까), 대체 가능성이 높아진다 (인터페이스가 작을 수록 구현이 쉬워지기 때문에 구현체를 대체하기가 더 쉬워진다).
의존관계 역전 원칙 DIP (Dependency Inversion Principle)
프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다. DI는 이 원칙을 구현하는 방법 중 하나이다.
쉽게 이야기해서 구현 클래스에 의존하지 않고, 인터페이스에 의존해야 한다는 뜻.
- 운전자는 자동차 역할에 의존해야지, K3나 테슬라같은 기종에 의존하면 안된다.
- 로미오를 연기하는 배우가 줄리엣의 대본에 의존해서 연기를 해야지, 줄리엣을 연기하는 배우가 바뀌었다고 해서 연기를 하지 못하게 되면 안된다.
즉 구현이 아닌 역할에 의존해야 한다는 뜻. 클라이언트가 인터페이스에 의존해야만 유연하게 구현체를 변경할 수 있다. 구현체에 의존하면 변경이 어려워진다!
OCP 내용에서 들었던 예시 코드를 보면, MemberService는 인터페이스에 의존하지만, 동시에 구현 클래스에도 의존한다. 구현 클래스를 직접 선택(의존)하기 때문! -> DIP 위반
public class MemberService
{
//둘 다 가능하다!
//private MemberRepository mr = new MemoryMemberRepository();
private MemberRepository mr = new JDBCMemberRepository();
}
즉 위 코드는 DIP를 위반했기 때문에, 확장 시 클라이언트의 코드를 변경하게 되어서 OCP를 위반하게 된다!
정리
- 객체 지향의 핵심은 다형성이다.
- 하지만 다형성 만으로는 쉽게 부품을 갈아 끼우듯이 개발할 수 없다.
- 다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경된다.
- 다시 말하면 다형성 만으로는 OCP, DIP를 지킬 수 없다!
- 뭔가가 더 필요하다...
'Spring > Spring core basic' 카테고리의 다른 글
[Spring core basic] 06 - 회원 도메인 개발 (0) | 2022.05.02 |
---|---|
[Spring core basic] 05 - 회원 도메인 설계 (0) | 2022.05.02 |
[Spring core basic] 04 - 객체 지향 설계와 Spring (0) | 2022.05.02 |
[Spring core basic] 02 - 좋은 객체 지향 프로그래밍이란? (0) | 2022.05.02 |
[Spring core basic] 01 - Spring이란? (0) | 2022.05.02 |
댓글