SOLID란?
SRP(Single Responsibility Principle) : 단일 책임 원칙
OCP(Opne Closed Principle) : 개방 폐쇄 원칙
LSP(Liskov Substitution Principle) : 리스코프 치환 원칙
ISP(Interface Segregation Priciple) : 인터페이스 분리 원칙
DIP(Dependency Inversion Principle) : 의존 역전 원칙
1.SRP(Single Responsibility Principle) : 단일 책임 원칙
어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다. - 로버트 C.마틴
위에서 남자 클래스는 역할과 책임이 너무 많다. 남자라는 클래스의 책임범위를 너무 방대하게 잡았기 때문에 하나의 클래스가 수정되어야 할 요소들도 그만큼 많아진다.
그래서 위에서처럼 남자 클래스를 4개로 나눠 볼 수 있다. 그리고 역할과 클래스 명도 딱 떨어지니 이해하기도 좋다. 다른 클래스의 영향도 적어진다. 코드로 살펴보자
메서드가 단일 책임 원칙을 지키지 않은 경우
class 강아지 {
final static Boolean 수컷 = true;
final static Boolean 암컷 = false;
Boolean 성별;
void 소변보다() {
if (this.성별 == 수컷) {
// 한쪽 다리를 들고 소변을 보다.
} else {
// 뒷다리 두 개를 굽혀 앉은 자세로 소변을 본다.
}
}
}
위에 강아지 클래스는 강아지가 수컷이냐 암컷이냐에 다라 소변보다() 메서드에서 분기 처리가 진행된다. 단일 책임 원칙을 적용해 코드를 리팩터링 하면
abstract class 강아지 {
abstract void 소변보다();
}
class 수컷강아지 extends 강아지 {
void 소변보다() {
// 한쪽 다리를 들고 소변을 본다.
}
}
class 암컷강아지 extends 강아지 {
void 소변보다() {
// 뒷다리 두 개로 앉은 자세로 소변을 본다.
}
}
위에 코드처럼 강아지라는 추상 클래스를 둔 뒤에 수컷과 암컷을 나눠 성별의 맞게 소변보다()를 재정의 하면 객체지향 4대 특성도 활용하며 더 객체지향다운 코드로 리팩터링 가능하다.
2. OCP(Opne Closed Principle) : 개방 폐쇄 원칙
소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 한다. - 로버트 C. 마틴
위에 말을 보면 이게 뭔 말인가 싶었는데 확장을 하는데 변경에 닫혀있어야 한다니..? 싶었는데
위에 다이어그램을 보면 운전자가 마티즈, 소나타 차량의 변화에 따라 운전에 영향을 받아야 하는가를 생각해보자. 현실 세계라면 당연히 어느 정도 변화가 있어야 하겠지만 객체지향 세계에서는 다른 해법이 있다.
위에 다이어그램처럼 상위 클래스 또는 인터페이스를 중간에 둠으로써 다양한 자동차가 생긴다고 해도 객체 지향 세계의 운전자는 운전 습관에 영향을 받지 않게 된다. 다양한 자동차가 생긴다고 하는 것은 자동차 입장에서는 확장에는 개발돼 있는 것이고 운전자 입장에서는 변화에 폐쇄되어 있는 것이다. 개방 폐쇄 원칙을 따르지 않는다고 해서 객체지향 프로그램을 구현하는 것이 불가능한 것은 아니지만 개방 폐쇄 원칙을 무시하고 프로그램을 작성하면 객체지향의 장점인 유연성, 재사용성, 유지보수성 등을 얻을 수 없다. 스프링 프레임워크를 공부하다 보면 개방 폐쇄 원칙을 교과서 적으로 활용하고 있음을 확인할 수 있다.
3. LSP(Liskov Substitution Principle) : 리스코프 치환 원칙
서브 타입은 언제나 자신의 기반 타입(Base type)으로 교환할 수 있어야 한다. - 로버트 C. 마틴
객체지향에서 상속은 조직도, 계층도가 아니라 분류도가 되어야 한다.
- 하위 클래스 is a kind of 상위 클래스 : 하위분류는 상위 분류의 한 종류이다.
- 구현 클래스 is able to 인터페이스 : 구현 분류는 인터페이스 할 수 있어야 한다.
분류도 처럼 하위에 존재하는 것들은 상위에 있는 것들의 역할을 하는 데 전혀 문제 가 없다. 고래가 포유류 또는 동물의 역할을 하는 것은 전혀 문제가 되지 않는다. 결국 리스코프 치환 원칙은 객체 지향의 상속이라는 특성을 올바르게 활용하면 자연스럽게 얻게 되는 것이다.
4. ISP(Interface Segregation Priciple) : 인터페이스 분리 원칙
클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다. - 로버트 C. 마틴
단일 책임원칙에서 제시한 해결책은 남자 클래스를 토막 내서 하나의 역할만 하는 다수의 클래스로 분할하는 것이었다. 그런데 이 방법만 존재할까?라는 생각이 든다면 그때 선택할 수 있는 방법이 인터페이스 분리 원칙이다. 인터페이스 분할 원칙이 제시하는 해결 책을 보면
남자 객체를 인터페이스로 나누는 것이다. 결론적으로 단일 책임원칙과 인터페이스 분할 원칙은 같은 문제에 대한 두 가지 다른 해결책이라고 볼 수 있다. 하지만 특별한 경우가 아니라면 단일 책임 원칙을 적용하는 것이 더 좋은 해결책이라고 할 수 있다.
인터페이스를 통해 메서드를 외부에 제공할 때는 최소한의 메서드만 제공해야 한다. 상위 클래스는 풍성할수록 좋고, 인터페이스는 작을수록 좋다.
5. DIP(Dependency Inversion Principle) : 의존 역전 원칙
- 고차원 모듈은 저 차원 모듈에 의존하면 안 된다. 이 두 모듈 모두 다른 추상화된 것에 의존해야 한다.
- 추상화된 것은 구체적인 것에 의존하면 안된다. 구체적인 것이 추상화된 것에 의존해야 한다.
- 자주 변경되는 구체 클래스에 의존하지 마라 - 로버트 C. 마틴
위에 다이어그램처럼 스노우타이어에 의존하고 있는 자동차 클래스가 있다. 만약 겨울이 지나 스노우타이어를 교체해야 한다면 자동차 클래스에서도 코드의 변화가 필요할 것이다. 이 경우 자동차 자신보다 더 자주 변하는 구체 클래스에 의존하지 마라 라는 원칙을 어겨버리게 된다.
위에처럼 자동차가 구체적인 타이어들이 아닌 추상화된 타이어 인터페이스에만 의존하게 함으로써 스노우타이어에서 일반 타이어로, 또는 다른 구체적인 타이어로 변경돼도 자동차에서는 코드의 변화가 필요 없게 된다. 이 설명은 개방 폐쇄 원칙에서도 나온 설명이다.
첫 번째 사진을 보면 스노우타이어가 그 무엇에도 의존하지 않는 클래스였는데, 아래 사진처럼 추상적인 타이어 인터페이스에 의존하게 됐다. 이게 바로 의존의 방향이 역전된 것이다.
이처럼 자신보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변하기 쉬운 거의 변화에 영향받지 않게 하는 것이 의존 역전 원칙이다.
'BE > Java' 카테고리의 다른 글
String vs StringBuffer vs StringBuilder (0) | 2022.11.07 |
---|---|
[Java] 자바11의 간단한 설명 (2) | 2022.10.31 |
자바의 대표적인 빌드 관리 툴 메이븐과 그레이들 (2) | 2022.08.10 |
일급 컬렉션 (First Class Collection) 이란? (0) | 2022.07.18 |
자바 제네릭스(generics)란? (0) | 2022.04.24 |