클라우드 낚시꾼

[Spring] 객체 지향 설계 원칙 SRP, DIP, OCP와 관심사의 분리 본문

BE Framework/SpringBasic

[Spring] 객체 지향 설계 원칙 SRP, DIP, OCP와 관심사의 분리

KanuBang 2024. 3. 12. 13:33
728x90

이 글에서는 객체 지향 설계 원칙 SRP, DIP, OCP를 어긴 예시와 이를 지키도록 고치며 SRP, DIP, OCP를 알아 볼 것이다.

그럼 시작해보자!!

1. SRP, DIP, OCP를 지키지 못한 주문 서비스 시스템 (OrderServiceImpl 클래스)

 

주문 서비스 다이어그램

 

주문 서비스가 할인 정책과 의존 관계를 맺고 있다. 할인 정책을 간단히 소개하자면, 소비자가 VIP 등급이면 FixDiscountPolicy는 가격에서 1000원을 할인해주고 RateDiscountPolicy는 가격의 10% 할인해준다. 그리고 두 할인 정책 모두 BASIC 등급이면 할인 정책이 적용되지 않는다. 이 할인 정책을 사용하는 OrderServiceImpl 클래스의 구현 코드를 확인해보자.

 

OrderServiceImpl 클래스

 

이 클래스에서는 주문을 생성하는 createOrder 메서드가 있다. 또한, 멤버로는 memberService와 discountPolicy(할인 정책)을 가지고 있다. 겉보기에는 문제 없어 보이지만, 이 코드는 SRP, DIP, OCP를 어겼다. 차근 차근 확인해보자.

 

SRP(단일 책임 원칙)

먼저, OrderServiceImpl은 너무 많은 책임을 가지고 있다. 역할을 실행하는 것 뿐만 아니라 멤버에 구현 객체를 생성하고 연결하는 것까지 담당하고 있다. 그렇기 때문에 SRP(단일 책임 원칙)을 어겼다고 볼 수 있다. 

 

DIP(의존 관계 역전 원칙)

DIP는 "추상화에 의존해야지 구체화에 의존하면 안된다."라는 원칙이다. 로미오와 줄리엣에서 로미오 배우가 상대 줄리엣 여배우(구체화)에 의존하지 않고 줄리엣이라는 역할(추상화) 자체에 의존하여 연기하는 것과 같은 맥락이다. 하지만, 위의 코드에서는 멤버 변수들이 구현체에 의존하고 있다. 예를 들자면, 멤버 discountPolicy가 FixDiscountPolicy라는 DiscountPolicy(할인 정책)의 구현체에 의존하고 있다.

(위의 클래스 다이어그램을 다시 확인해주세요😃)

 

OCP(개방-폐쇄 원칙)

위 코드에서 만약 discountPolicy를 FixDiscountPolicy에서 RateDiscountPolicy로 바꿀라면 어떻게 해야 할까? 클라이언트(OrderServiceImpl)의 소스 코드를 수정하면 된다. 하지만, 클라이언트 코드의 변경은 OCP 원칙을 어긴 것이다. OCP란 기존 코드를 변경하지 않고 확장 할 수 있음을 의미한다.

(OCP: 소프트웨어 개체는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다.)


2. 해결책: 관심사의 분리 

관심사의 분리

연극이 어떻게 구성되는지 생각해보자. 연극은 배우뿐만 아니라 뒤에 수많은 공연 기획자들에 의해 만들어진다. 로미오 배우는 로미오 역할 수행에 충실할 뿐 줄리엣 역할의 캐스팅을 담당하지 않는다. 캐스팅은 공연 기획자가 담당한다. 이것을 관심사의 분리라고 한다. 즉, 관심사의 분리는 각자의 책임을 확실하게 분리하는 것을 의미한다.


3. 주문 서비스 시스템에 (OrderServiceImpl 클래스) 관심사의 분리 적용하기

AppConfig

 

AppConfig 클래스는 구현 객체를 생성하고, 연결시켜주는 역할을 한다. 로미오와 줄리엣에서 줄리엣 여배우를 캐스팅하고 계약을 주선하는 공연기획자로 생각하면 된다. 즉, 큰 틀에서 구성영역(구성자)을 담당하고 있다.

 

OrderServiceImpl

 

AppConfig라는 구성자가 구현 객체의 생성과 연결을 담당하니 기존 주문 서비스 OrderServiceImpl 클래스가 SRP, DIP, OCP를 지킬 수 있게 되었다. 차근 차근 확인해보자. (목차 1로 돌아가 기존 OrderServiceImpl과 어떤 차이가 있는 지 확인해보자.)

SRP(단일 책임 원칙)

AppConfig 구성자가 구현 객체를 생성하고 외부에서 주입(연결) 해주기에 OrderServiceImpl은 주입 받은 객체로 역할만 수행할 수 있게 되었다. 이로써 역할 수행이라는 단일한 책임만 지게 되어 SRP를 지킬 수 있게 되었다.

DIP(의존 관계 역전 원칙)

AppConfig 구성자가 구현 객체를 생성하고 외부에서 주입해주기에 OrderServiceImpl은 추상화에만 의존할 수 있게 되었다. 위의 코드에서 구현체에 직접적으로 의존하고 있는 코드는 없다. 그러므로, DIP도 지키게 되었다.

OCP(개방-폐쇄 원칙)

만약, 할인 정책을 RateDiscountPolicy에서 FixDiscountPolicy로 바꿀라면 어떻게 해야할까? AppConfig에서 discountPolicy()메서드가 반환하는 객체를 FixDiscountPolicy로 수정하면 된다. 이 과정에서 OrderServiceImpl에서 코드 변경은 일어나지 않는다. 이럼으로써 OCP를 지킬 수 있게 되었다.


4. 구성 영역과 사용 영역 

구조의 변화

 

왼쪽은 주문 서비스 초기 코드의 구조이다. 초기에는 사용 영역과 구성 영역이 혼재되어 SRP, DIP, OCP를 지킬 수 없었다. 하지만, 오른쪽에서는 AppConfig라는 구성자가 도입된 이후 구성 영역과 사용 영역이 명확하게 구분되어 SRP, DIP, OCP를 지킬 수 있게 되었다.

후에 다루게 될 IoC, DI, 스프링 컨테이너도 구성자로서 AppConfig와 같이 구성 영역과 사용 영역을 명확하게 분리하게 하여 객체 지향 설계 원칙을 지킬 수 있도록 도와준다. 자세한 내용는 후에 다루도록 하겠다. 


5. 출처

 

스프링 핵심 원리 - 기본편 강의 - 인프런

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 백엔드 개발자가 되어보세요! 📢

www.inflearn.com

728x90