내돈내고 내가 공부한것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼겸 상세히 기록하고 얕은부분들은 가겹게 포스팅 하겠습니다.
SOLID 원칙
SRP : 단일 책임 원칙
OCP : 개방 폐쇄 원칙
LSP : 리스코프 치환 원칙
ISP : 인터페이스 분리 원칙
DIP : 의존관계 역전 원칙
SRP (Single Responsibility Principle)
단일 책임 원칙이라 부른다.
한 클래스는 하나의 책임만을 가져야 한다.
하지만 하나의 책임이라는 것이 좀 모호하다. 책임이라는게 클 수도 있고, 작을 수도 있기 때문이다.
예를들어 자동차를 생각해보자.
자동자 전체를 하나의 책임으로 본다면 운전, 트렁트, 좌석 등등 모든 기능을 자동차의 기능으로 보고 하나의 큰 책임이라 할 수도 있고,
운전을 하나의 책임, 트렁크도 하나의 책임, 좌석도 하나의 책임으로 세부적으로 본다면 이또한 각자의 첵임이라 할 수 있다.
따라서 문맥과 상황에 따라 다르다.
중요한 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙 잘 따른 것이다.
예를 들어서 UI하나 변경하는데, SQL 코드부터 시작해서 애플리케이션 다 고쳐야 한다면 단일책임 원칙을 잘 지키지 못한 것이다.
OCP (Open Closed Principle)
소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야 한다.
- 확장에 대해 열려 있다: 요구사항이 변경될 때 새로운 동작을 추가하여 애플리케이션의 기능을 확장할 수 있다.
- 수정에 대해 닫혀 있다: 기존의 코드를 수정하지 않고 애플리케이션의 동작을 추가하거나 변경할 수 있다.
어떻게? 확장을 하려면 당연히 기존 코드 변경해야하지 않나?
다형성을 활용해보자!
인터페이스를 구현한 새로운 클래스(구상 클래스)를 하나 만들어서 새로운 기능을 구현했다고 해보자.
이런 새로운 클래스를 만드는 것은 기존 코드를 변경하는 것이 아니다. 인터페이스는 그대로 있고 구현체가 만들어진 것 이다.
역할과 구현의 분리를 통한 문제점을 알아보자.
▶ 기존 코드
public class MemberService {
private MemberRepository memberRepository = new MemoryMemberRepository();
}
▶ 변경 후
public class MemberService {
//private MemberRepository memberRepository = new MemoryMemberRepository();
private MemberRepository memberRepository = new JdbcMemberRepository();
}
위 코드의 문제점은 MemberService 라는 Client에서 구현 클래스를 바꾸기 위해 코드를 변경했다는 점 이다.
원래 new MemoryMemberRepository() 에서 new JdbcMemberRepository() 로 분명 코드가 변경되었다.
다형성을 적용했음에도 불구하고 OCP에서 C에 해당하는 변경에 닫혀있지 못했다고 할 수 있다. OCP 원칙을 지킬수 없는 상황이다.
이를 해결하기 위해서는!
객체를 생성하고, 연관관계를 맺어주고, 별도의 조립과 설정을 해주는 것이 필요하다
바로 Spring 이다!
LSP (Liskov Substitution Principle)
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다.
즉, 하위 타입은 상위 타입을 대체할 수 있어야 한다는 것이다.
해당 객체를 사용하는 클라이언트는 상위 타입이 하위 타입으로 변경되어도, 차이점을 인식하지 못한 채 상위 타입의 퍼블릭 인터페이스를 통해 서브 클래스를 사용할 수 있어야 한다는 것이다.
다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것, 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체는 믿고 사용하려면, 이 원칙이 필요하다.
단순히 컴파일에 성공하는 것을 넘어서는 이야기
예) 자동차 인터페이스의 엑셀은 앞으로 가라는 기능이다. 뒤로 가게 구현한다면 LSP를 위반한 것! 느리더라도 앞으로 가야함
(단순히 구현여부만 따지는 것이 아님, 기능적으로 올바르게 구현했느냐를 보는 것임)
ISP (Interface Segregation Principle)
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
자동차 인터페이스 ➡ 운전 인터페이스, 정비 인터페이스로 분리
사용자 클라이언트 ➡ 운전자 클라이언트, 정비사 클라이언트로 분리
위와같이 분리하면 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않게된다.
(정비 인터페이스가 변경해야하는 경우 정비 인터페이스만 바꾸면 된다, 운전자 인터페이스까지 변경할 필요가 없어짐)
인터페이스가 명확해지고, 대체 가능성이 높아게되는 것 이다!
DIP (Dependency Inversion Principle)
프로그래머는 "추상화에 의존해야지 구체화에 의존하면 안된다" 의존성 주입은 이 원칙을 따르는 방법 중 하나다.
쉽게 이야기해서 구현 클래스(구상 클래스) 에 의존하지 않고 인터페이스에 의존하라는 뜻이다.
앞에서 이야기한 역할(Role)에 의존하게 해야한다는 것과 같다.
객체 세상도 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다!
구현체에 의존하게 되면 변경이 아주 어려워진다.
하지만 위에서 OCP를 설명할때 나온 MemberService는 인터페이스에 의존하지만, 구현 클래스도 동시에 의존했다.
MemberService 클라이언트가 구현 클래스를 직접 선택했었다. 다음 코드를 봐보자!
MemberRepository m = new MemoryMemberRepository();
MemberService 클라이언트가 인터페이스 m 과 구현체 MemoryMemberRepository 양쪽 모두에 의존하고 있다.
이는 명백한 DIP 위반이다.
이부분은 Spring에서 DI(의존성 주입)을 통하여 해결할 수 있다.
추가적으로 DIP가 궁금하다면 팩토리 페턴을 한번 공부해보시길 강추합니다! 딱 DIP에 해당되는 내용입니다.
'BackEnd > Spring' 카테고리의 다른 글
[Spring] 스프링 핵심 원리 이해2 - 객체 지향 원리 적용 (0) | 2022.02.01 |
---|---|
[Spring] 스프링 핵심 원리 이해1 - 예제 만들기 (0) | 2022.01.31 |
[Spring] 스프링은 객체 컨테이너 (0) | 2022.01.14 |
[Spring] AOP : Aspect Oriented Programming (0) | 2022.01.14 |
[Spring] 스프링 DB 접근 기술 (0) | 2022.01.14 |
댓글