스프링/핵심원리-기본

스프링과 객체지향, SOLID원칙

eunkyung 2022. 9. 4. 17:57

*스프링과 객체지향

스프링은 다형성을 극대화해서 이용 가능하게 도와줌. IoC(제어의 역전), DI(의존관계 주입)는 다형성 활용해 역할/구현을 편리하게 다룰 수 있게 지원함, 

~> 스프링 사용 시 구현을 편리하게 변경 가능. (입문 강의에서 MemberRepository 구현체 변경 매우 편리했음)

 

다형성과 더불어 SOLID를 이해해야 함  //면접에서 물어볼 수도 O. 

 

>SOLID, 좋은 객체 지향 설계의 5가지 원칙

1)SRP 단일 책임 원칙(Single responsibility principle):

-한 클래스는 하나의 책임만, 하나의 책임이라는 것은 문맥과 상황에 따라 다르다. 책임 범위를 적절하게 조절해야 함.

-중요한 판단 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것(변경 시 다른 여러가지 많이 변경할 필요가 없다면 원칙을 잘 따른것).  예) UI 변경, 객체의 생성과 사용을 분리

 

2) OCP 개방-폐쇄 원칙(Open/closed principle):   ✨

-소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다(확장 시 코드 변경이 필요할텐데 어떻게?)

-다형성을 활용(역할과 구현 분리) -> 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현.

*OCP 개방-폐쇄 원칙 문제점

->MemberService 클라이언트가 구현 클래스를 직접 선택.

MemberRepository m = new MemoryMemberRepository(); //1
MemberRepository m = new JdbcMemberRepository(); //2

->구현 객체를 변경하려면 클라이언트 코드를 변경해야 한다. 변경에 닫혀있어야 하지만... 1->2로 바꿀때 1->2로 코드 변경 필요. 구현체 적용시 OCP가 지켜지지 않음. 클라이언트가 변경을 직접해줘야 함.=> 다형성을 사용했지만 OCP 원칙을 지킬 수 없다.

->어떻게 해결해야 하나? =>객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자가 필요. 스프링이 해줌

 

3)LSP 리스코프 치환 원칙(Liskov substitution principle):

-프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.

-(ex)엑셀 인터페이스가 있을 때 엑셀을 밟으면 +10이 된다는 기능이 있다면, 엑셀 구현 객체는 엑셀 밟으면 +10 되게 구현해야 함. 만약 -10되게 하면 컴파일되지만 엑셀 밟으면 +10되야 한다는 규약에 어긋남(=>LSP원칙에 어긋남). 단순한 컴파일 상 문제가 아니라 기능적으로 엑셀의 기능을 보장해줘야 한다는 것. 느리더라도 앞으로 가면 원칙에 어긋나지 않은 것.

-다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 함. 다형성을 지원하기 위한 원칙으로 인터페이스를 구현한 구현체 믿고 사용 가능.

 

4)ISP 인터페이스 분리 원칙(Interface segregation principle):

-특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.

-(ex)자동차라는 인터페이스 하나보다 운전/정비 인터페이스 두 개로 분리하는게 나음. 사용자 클라이언트를 운전자/정비사 클라이언트로 분리 가능. 분리하면 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않음

-인터페이스가 명확해지고, 대체 가능성이 높아짐.

 

5)DIP 의존관계 역전 원칙(Dependency inversion principle):  ✨

-프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안된다.”,  의존성 주입은 이 원칙을 따름.

->쉽게 이야기해서 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻(MemberService가 MemberRepository 인터페이스에 대해서만 알고, 구현체인 JdbcMemberRepository에 대해서는 몰라야 함.)

-역할에 의존하게 해야 한다는 것. 객체 세상도 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경 가능. 구현체에 의존하게 되면 변경이 어려움.

->ex)로미오와 줄리엣 역할에서 로미오 역할의 원빈이 줄리엣 역할 배우 중 김태희랑만 연습하고, 김태희랑만 공연하겠다고 하면 X. 대체 가능성이 없는 것. 줄리엣 역할과 공연가능해야 함.

-그런데 OCP에서 설명한 MemberService는 인터페이스에 의존하지만(=알고 있으면), 구현 클래스도 동시에 의존.

-> MemberRepository m = new MemoryMemberRepository(); 이코드를 보면 구현체인 MemoryMemberRepository를 MemberService 클래스 내에서 할당받아 사용 중. 이는 MemoryMemberRepository 역시 알고 있기에 의존한 것.

-MemberService 클라이언트가 구현 클래스를 직접 선택하기에 인터페이스인 MemberRepository뿐만 아니라 구현체인 MemoryMemberRepositor도 앎. =>DIP 위반 정리

 

*객체 지향의 핵심은 다형성 이지만 이것만으로 변경이 쉽게 개발할 수 없고  OCP, DIP를 지킬 수 없음. 구현 객체를 변경할 때 클라이언트 코드도 함께 변경됨.

 

다시 스프링으로 돌아가보면,

스프링은 다음 기술로 다형성 + OCP, DIP 가능하게 지원.

-DI(Dependency Injecton): 의존 관계 주입.

-DI 컨테이너 제공.

클라이언트 코드의 변경 없이 기능 확장 / 쉽게 부품을 교체하듯이 개발

 

=>이상적으로는 모든 설계에 인터페이스를 부여하는게 좋음. 구현체 안정해도 인터페이스 개발 먼저 하고 구현체 선택 가능.  하지만 인터페이스 도입 시 추상화라는 비용이 발생.

->기능을 확장할 가능성이 없다면, 구체 클래스를 직접 사용. 향후 꼭 필요할 때 리팩터링해서 인터페이스를 도입하는 것도 방법. 

 

+++객체 지향의 사실과 오해, 토비의 스프링, 자바 ORM 표준 JPA 프로그래밍 책 추천.+++

 

 

 

 

 

'스프링 > 핵심원리-기본' 카테고리의 다른 글

싱글톤 컨테이너  (0) 2022.10.02
웹 애플리케이션과 싱글톤  (0) 2022.09.21
스프링 객체지향 설계 적용 (2)  (0) 2022.09.18
스프링에 객체지향 원리 적용  (0) 2022.09.18
스프링 예제  (0) 2022.09.06