작심 365

5장-객체 지향 설계 5원칙 - SOLID 본문

책/스프링 입문을 위한 자바 객체 지향의 원리와 이해

5장-객체 지향 설계 5원칙 - SOLID

eunKyung KIM 2023. 10. 3. 00:53
더보기

해당 글은 스프링 입문을 위한 자바 객체 지향의 원리와 이해 라는 책을 읽고 정리한 내용 입니다. 


이번 장에서는 객체 지향 프로그램을 잘 설계하기 위한 5원칙인 SOLID를 설명합니다.

 

 

좋은 소프트웨어 설계는 결합도는 낮추고 응집도는 높이는 것이 바람직 합니다.

 

SRP - 단일 책임 원칙

"어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다" - 로버트 C.마틴

 

단일 책임 원칙이란 말 그대로 책임(역할)을 분리하는 것을 말합니다. 

 

남자 클래스와 의존 관계에 있는 다른 클래스들

해당 그림을 보면 남자라는 클래스에서 하는 역할이 너무 많은것을 볼수 있습니다. 예를 들어 어느날 여자 친구와 해어졌다고 하면 더이상 이 남자 클래스는 기념일챙기기 라는 것을 수행할 필요가 없지만 어머니,직장상사,소대장 까지 이것에 대한 영향을 받아야 합니다.

불필요한 정보들이 얽혀있는것은 좋은것이 아닙니다. 따라서 이런 경우 역할을 분리하는것을 권장하고 그것이 단일 책임 원칙입니다.

 

남자 클래스에 단일 책임 원칙을 적용해 여러 개의 클래스로 분리

남자라는 하나의 클래스가 역할가 책임에 따라 네개의 클래스로 쪼개진 것을 볼 수 있습니다. 역할과 클래스명도 딱 떨어져서 이해하기도 훨씬 편합니다. 그리고 여자친구와 남자친구 클래스 사이에 변화가 생겨도 다른 클래스 관계들에는 전혀 영향을 주지 않습니다.

예시는 클래스의 분할에 대해서만 이야기 했지만 단일 책임 원칙은 속성, 메서드, 패키지, 모듈, 컴포넌트, 프레임워크 등에서도 적용할 수 있습니다.

 

 

OCP - 개방 폐쇄 원칙

"소프트웨어 엔티티(클래스,모듈,함수 등)는 확장에 대해서는 열려있어야 하지만 변경에 대해서는 닫혀 있어야 한다." - 로버트 C.마틴

 

 

개방 폐쇄 원칙에 위배되는 경우

 

위 예시를 보면 차종을 변경할때마다 다른 클래스를 의존하게 됩니다. 그렇게 되면 자동차 종류에 따라 클래스가 만들어 지게 됩니다. 공통된 특징들이 있는데 그걸 따로 뽑지 않고 각자 따로 만드는 것은 자바의 객체지향 개념과도 어울리지 않습니다.

 

 

 

개방 폐쇄 원칙을 적용한 경우

개방 폐쇄 원칙을 적용해서 다시 설계를 해보면 자동차 라는 상위 클래스나 인터페이스를 두고 자동차 종류별로 해당 클래스를 상속받는 형태로 구현할수 있습니다. 이렇게 상위 클래스 또는 인터페이스를 중간에 두고 필요에 의해 얼마든지 확장해서 사용하는 것이 더 바람직한 설계라고 볼 수 있습니다.

 

LSP - 리스코드 치환 원칙

"서브 타입은 언제나 자신의 기반 타입(base type)으로 교체할 수 있어야 한다." - 로버트 C.마틴

 

객체 지향에서의 상속은 조직도나 계층도가 아닌 분류도가 돼야 된다고 합니다. 

따라서 객체 지향의 상속은 다음의 조건을 만족해야 합니다.

  • 하위 클래스 is a kind of 상위 클래스 - 하위 분류는 상위 분류의 한 종류다.
  • 구현 클래스 is able to 인터페이스 - 구현 분류는 인터페이스할 수 있어야 한다.

 

"하위 클래스의 인스턴스는 상위형 객체 참조 변수에 대입해 상위 클래스의 인스턴스 역할을 하는 데 문제가 없어야 한다."

 

리스코프 치환 원칙 위반 사례 - 계층도/조직도

 

 

 

리스코프 치환 원칙 적용 사례 - 분류도

 

 

 

ISP - 인터페이스 분리 원칙

"클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다." - 로버트 C.마틴

 

 

앞선 단일 책임 원칙에서 다양한 책임을 가진 남자 클래스를 세분화해서 여러 클래스로 쪼갠적이 있었습니다. 그런데 이런 방식 말고도 우리는 다른 방식을 사용해서 해결할수도 있습니다. 바로 ISP 원칙을 적용하는 것입니다. 이름에서 알수 있듯이 인터페이스를 사용하는 방식 입니다.

인터페이스 분할 원칙을 적용한 남자 클래스

 

단일 책임 원칙(SRP) 과 인터페이스 분할 원칙 (ISP) 은 같은 문제에 대한 두가지 다른 해결책이라고 볼 수 있습니다.

프로젝트 요구사항과 설계자의 취향에 따라 둘중 하나를 선택해서 설계할수 있지만 보통은 단일 책임 원칙을 적용하는 것이 더 좋은 해결책 이라고 합니다.

 

 

 

 

 

인터페이스 분할 원칙을 이야기할때 인터페이스 최소주의 라는 원칙이 등장하는데 이는 인터페이스를 통해 메서드를 외부에 제공할 때는 최소한의 메서드만 제공하라는 것 입니다.

 

a) 빈약한 상위 클래스
b) 풍성한 상위 클래스

 

 

 

 

DIP - 의존 역전 원칙

"고차원 모듈은 저차원 모듈에 의존하면 안 된다. 이 두 모듈 모두 다른 추상화된 것에 의존해야 한다."

"추상화된 것은 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상화된 것에 의존해야 한다."

"자주 변경되는 구체(Concrete) 클래스에 의존하지 마라" - 로버트 C.마틴

 

 

의존 역전 원칙 적용 전(자주 변경되는 구체 클래스에 의존)

자동차와 스노우타이어 사이에는 그림처럼 의존 관계가 있습니다. 자동차가 스노우타이어를 의존하는 관계입니다.

그런데 자동차를 몇년 타다보면 타이어를 교체해야 되는 시기가 오고 이때 다른 타이어로 교체하고자 한다면 자동차는 스노우타이어를 의존하고 있기때문에 타이어가 변경될때마다 의존 관계를 바꿔줘야 합니다. 이렇게 자주 변경될수 있는 클래스에 의존하는것은 좋지 않은 설계입니다.

 

의존 역전 원칙 적용 후

 

타이어라는 interface를 두고 여러 종류의 타이어들을 구현 클래스로 만들게 되면 자동차는 타이어 인터페이스에만 의존이 가능해집니다.

 

 

따라서 의존 역전 원칙을 다시 해석해보면 "자신보다 변하기 쉬원 것에 의존하지 마라." 라고 해석할 수 있습니다.

 

상위 클래스일수록, 인터페이스일수록, 추상 클래스일수록 변하지 않을 가능성이 높기 때문에 하위 클래스나 구체 클래스가 아닌 상위 클래스, 인터페이스, 추상 클래스를 통해 의존하라는 것이 바로 의존 역전 원칙입니다.

 

 

 

 

Comments