[Java] 객체 지향 설계의 5가지 원칙 - SOLID

토끼는 개발개발·2022년 2월 27일
1

Java

목록 보기
1/33
post-thumbnail

객체 지향 설계의 5가지 원칙 - SOLID

  • SRP(Single Responsibility Principle): 단일 책임 원칙
  • OCP(Open Closed Priciple): 개방 폐쇄 원칙
  • LSP(Listov Substitution Priciple): 리스코프 치환 원칙
  • ISP(Interface Segregation Principle): 인터페이스 분리 원칙
  • DIP(Dependency Inversion Principle): 의존 역전 원칙

클린코드로 유명한 로버트 마틴이 좋은 객체 지향 설계의 5가지 원칙을 정리했다.
이것이 바로 위의 SOLID이다.



1. SRP: 단일 책임 원칙

SRP(Single Responsibility Principle)

"한 클래스는 하나의 책임만 가져야 한다."

하나의 책임이란 말은 문맥과 상황에 따른 모호한 말이다. 중요한 기준은 '변경'이다.
변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이라 말할 수 있다.

SRP 원칙을 적용하면 다른 클래스들이 서로 영향을 미치는 연쇄작용을 줄일 수 있다.
즉, 응집도(cohesion)는 높이고 결합도(coupling)은 낮출 수 있다.

위의 남자 클래스에 SRP를 적용한다고 가정해보자.
다음과 같이 책임을 분배하여 클래스를 분리할 수 있을 것이다.

이렇게 책임을 적절하게 분배함으로써 코드의 가독성 향상, 유지보수 용이라는 이점을 가질 수 있다.




2. OCP: 개방 폐쇄 원칙

OCP(Open Closed Priciple)

"소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀 있어야 한다."

이는 요구사항의 변경이나 추가사항의 발생하더라도, 기존 구성요소는 수정이 일어나지 말아야하며 쉽게 확장이 가능하여 재사용할 수 있어야 한다는 뜻이다.

로버트 마틴은 OCP는 관리가 용이하고 재사용 가능한 코드를 만드는 기반이며, OCP를 가능케 하는 중요한 메커니즘은 추상화(Abstraction)와 다형성(Polymorphism)이라고 설명한다.
OCP는 객체지향의 장점을 극대화하는 아주 중요한 원리이다.

위의 운전자 클래스는 마티즈 클래스와 소나타 클래스의 변화에 따라 행동이 의존적으로 변하게 된다.
다음과 같이 마티즈 클래스와 소나타 클래스 위에 자동차라는 상위 클래스를 두면, 운전자는 마티즈와 소나타 클래스의 변경사항에 영향을 받지 않을 수 있다.

이처럼 클래스를 설계할 때 변할 부분과 변하지 않을 부분을 명확히 구분해야한다.
변할 수 있는 부분은 추상화하여 상속하는 클래스가 의존할 수 있게 코드를 작성한다.




3. LSP: 리스코프 치환 원칙

LSP(Listov Substitution Priciple)

"서브 타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다."

프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다. 이는 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것을 말하며, 다형성을 지원하기 위한 원칙이다. 인터페이스를 구현한 구현체는 믿고 사용하려면 이 원칙이 필요하다.

상속을 잘 활용하고 있다면, 이미 LSP는 잘 구현하고 있는 것이다.

  • LSP를 잘 구현한 사례 : 박쥐는 포유류의 역할을 할 수 있다.

  • LSP 위반 사례 : 딸은 아버지의 역할을 할 수 없다.

쉬운 예시를 하나 더 들자면, 자동차 인터페이스의 엑셀은 앞으로 가게 하는 기능인데 이를 뒤로 가게 구현하면 LSP를 위반한 것이다. 느리더라도 앞으로 가게 구현해야 한다.




4. ISP: 인터페이스 분리 원칙

ISP(Interface Segregation Principle)

"클라이언트는 자신이 사용하지 않는 메소드에 의존 관계를 맺으면 안 된다."

이는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다는 원칙이다.

다시말해, 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다는 뜻으로, 하나의 큰 인터페이스를 상속 받기 보다는 인터페이스를 구체적이고 작은 단위들로 분리시켜 꼭 필요한 인터페이스만 상속하자는 의미이다.

SRP가 클래스의 단일책임을 강조했다면 ISP는 인터페이스의 단일책임을 강조한다.

위의 SRP 예제에서 남자 클래스를 단일 책임을 갖는 클래스들로 나누었다. 이것은 너무 많은 클래스 구현을 불러 온다.

ISP를 적용하면 다양한 역할을 인터페이스로 만들고, 남자 클래스는 그 인터페이스를 구현한 클래스로 만들 수 있다.




5. DIP: 의존관계 역전 원칙

DIP(Dependency Inversion Principle)

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

쉽게 이야기해서 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻이다. 구현체에 의존하게 되면 변경이 아주 어려워진다.

즉, 인터페이스를 적극적으로 활용하라는 의미이다.

자동차는 스노우타이어에 의존하고 있다.

해당 관계를 타이어 인터페이스로 역전 시킨다. 이것이 의존성 주입(DI)이다.

상위클래스일 수록, 인터페이스일 수록, 추상 클래스일 수록 변하지 않을 가능성이 크다. 하위클래스나 구체클래스가 아닌 더 추상적인 것에 의존하라는 것이 의존 역전 원칙이다.

✔ 의존성 주입(Dependency Injection, DI): 외부에서 두 객체 간의 관계를 결정하고 주입하는 것.



Ref.
스프링 핵심원리(김영한 강사님)
https://jaeyeong951.medium.com/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-5%EC%9B%90%EC%B9%99-solid-ac7d4d660f4d
https://sehun-kim.github.io/sehun/solid/

profile
하이 이것은 나의 깨지고 부서지는 기록들입니다

1개의 댓글

comment-user-thumbnail
2024년 4월 8일

잘 읽고 갑니다!

답글 달기