[Java] 객체지향 설계 5원칙 - SOLID란 무엇일까?
객체지향 설계 5원칙 SOLID
객체 지향 설계의 정수라고 할 수 있는 5원칙이 집대성됐는데, 바로 SOLID
입니다.
SRP(Single Responsibility Principle): 단일 책임 원칙
OCP(Open Closed Priciple): 개방 폐쇄 원칙
LSP(Listov Substitution Priciple): 리스코프 치환 원칙
ISP(Interface Segregation Principle): 인터페이스 분리 원칙
DIP(Dependency Inversion Principle): 의존 역전 원칙
SRP - 단일 책임 원칙
어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다. - 로버트 C.마틴
위의 남자 클래스
를 보면 그냥 보아도 이 사람이 해야 할 역할
, 책임
이 매우 많은 것을 볼 수 있습니다. 즉, 남자
라고 클래스의 범위를 너무 애매하게 잡았기 때문에 하나의 클래스가 많은 역할과 책임
을 가지게 된 것인데요.
그래서 위처럼 남자
라는 것을 각 역할에 맞게 잘 분리하면 역할과 책임
이 알맞게 잘 분리된 것을 볼 수 있습니다.
메소드가 SRP를 지키지 못한 경우
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대 특성인 캡상추다와 가장 관계가 깊은 것은 바로 모델링 과정을 담당하는 추상화임을 알 수 있습니다.
OCP - 개방 폐쇄 원칙
소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 한다. - 로버트 C.마틴
즉, 자신의 확장에는 열려 있고, 주변의 변화에 대해서는 닫혀 있어야 한다.
위의 그림을 보면 운전자
가 기어가 수동 or 자동
이냐에 따라 행동이 달라지는 것을 볼 수 있는데요. 이렇게 어떤 변화가 있을 때 바로 운전자에게 영향이 오기 때문에 이러한 설계는 개방 폐쇄 원칙
에 위배됩니다.
이렇게 상위 클래스 또는 인터페이스를 중간에 둠으로써 다양한 자동차가 생긴다고 해도 객체 지향 세계의 운전자는 운전 습관에 영향을 받지 않게 됩니다. 다양한 자동차가 생긴다고 하는 것은 자동차 입장에서는 자신의 확장에는 개방되어 있는 것이고, 운전자 입장에서는 주변의 변화에 폐쇄돼 있는 것입니다.
데이터베이스의 개방 폐쇄 원칙의 아주 좋은 예
JDBC
가 개방 폐쇄 원칙
의 가장 좋은 예입니다. 데이터베이스가 MySQL에서 오라클로 바뀌더라도 Connection을 설정하는 부분만 변경해주면 됩니다.
즉, 자바 애플리케이션은 데이터베이스라고 하는 주변의 변화에 닫혀 있는 것
입니다. 데이터베이스를 교체한다는 것은 데이터베이스가 자신의 확장에는 열려 있다는 것입니다.
개방 폐쇄 원칙을 무시하고 프로그램을 작성하면 객체지향 프로그래밍의 가장 큰 장점인 유연성, 재사용성, 유지보수성 등을 얻을 수 없다.
따라서 객체지향 프로그래밍에서 개방 폐쇄 원칙은 반드시 지켜야 할 원칙이다.
LSP - 리스코프 치환 원칙
서브 타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다. - 로버트 C.마틴
하위 클래스 is a kind of 상위 클래스
- 하위 분류는 상위 분류의 한 종류다.구현 클래스 is able to 인터페이스
: 구현 분류는 인터페이스할 수 있어야 한다.
위의 두 문장을 잘 지키고 있다면 이미 리스코프 치환 원칙
을 잘 지키고 있다고 할 수 있습니다.
리스코프 치환 원칙 위배
아버지 - 딸(계층도/조직도)
리프코프 치환 원칙 만족
동물 - 펭귄 구조(분류도)
즉, 하위 클래스의 인스턴스는 상위형 객체 참조 변수에 대입해 상위 클래스의 인스턴스 역할을 하는데 문제가 없어야 한다.
ISP - 인터페이스 분리 원칙
클라이언트는 자신이 사용하지 않는 메소드에 의존 관계를 맺으면 안 된다. - 로버트 C.마틴
단일 책임 원칙(SRP)
에서는 하나의 역할(책임)만 하도록 다수의 클래스로 분할하였습니다
인터페이스 분리 원칙
은 각 역할에 맞게 인터페이스
로 분리하는 것입니다.(ex 어머니한테는 아들, 여자친구한테는 남자친구)
결론적으로는 단일 책임 원칙(SRP)과 인터페이스 분할 원칙(ISP)은 같은 문제에 대한 두 가지 다른 해결책이라고 볼 수 있다.
하지만 특별한 경우가 아니라면 단일 책임 원칙을 적용하는 것이 더 좋은 해결책이라고 할 수 있다.
상위 클래스는 풍성할수록 좋다.
- 풍성할수록 하위 클래스에게 많은 기능을 확장시켜주는 것이고, 형변환, 코드 중복을 줄여줍니다.
인터페이스 내에 메소드는 최소한 일수록 좋다.
- 인터페이스는 하위 클래스에게 구현을 강제하도록 하는 역할입니다. 즉, 최소한의 기능만 제공하면서 하나의 역할에 집중하라는 뜻입니다.
DIP - 의존 역전 원칙
고차원 모듈은 저차원 모듈에 의존하면 안 된다. 이 두 모듈 모두 다른 추상화된 것에 의존해야 한다.
추상화된 것은 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상화된 것에 의존해야 한다.
자주 변경되는 구체(Concrete) 클래스에 의존하지 마라 - 로버트 C.마틴 -
자동차가 타이어에 의존하면 어떻게 될까요? 자동차 타이어는 자주 바뀌게 되는 것 중 하나입니다. 이렇게 자주 바뀌는 것에 의존하면 자동차는 영향을 받게 되어 있습니다. 즉, 자동차 자신보다 더 자주 변하는 스노우타이어에 의존하기에 좋지 않음을 알 수 있습니다.
자동차가 구체적인 타이어가 아닌 추상화된 타이어 인터페이스에만 의존하게 함으로써 타이어가 변경되어도 자동차가 영향을 받지 않습니다.
이처럼 자신보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변하기 쉬운 것의 변화에 영향받지 않게 하는 것이 의존 역전 원칙이다.
상위 클래스일수록, 인터페이스일수록, 추상 클래스일수록 변하지 않을 가능성이 높기에 하위 클래스나 구체 클래스가 아닌 상위 클래스, 인터페이스, 추상 클래스를 통해 의존하라는 것이 바로 의존 역전 원칙이다.