좋은 객체지향 설계를 위한 다섯 개의 원칙이 존재한다.
[ SOLID ]
1. SRP : 단일 책임 원칙 (Single Responsibility Principle)
2. OCP : 개발-폐쇄 원칙 (Open/closed Principle)
3. LSP : 리스코프 치환 원칙 (Liskov Substitution Principle)
4. ISP : 인터페이스 분리 원칙 (Interface Segregation Principle)
5. DIP : 의존관계 역전 원칙 (Dependency Inversion Principle)
SRP (Single Responsibility Principle)
1) 하나의 클래스는 하나의 책임만 가져야 한다. (하나의 책임이란 것은 모호한 개념)
2) 중요한 판단의 기준은 변경이다.
-> 코드의 변경이 있을 때 파급 효과가 적으면 적을수록 단일 책임 원칙을 잘 따른 것이다.
위의 설명과 같이, 만약 프로젝트에서 하나의 기능을 변경하는데, 소스 전체를 고쳐야 하는 상황이 온다면,
해당 프로젝트는 SRP 원칙을 잘 지키고 있는 것읻 아니다.
OCP (Open/closed Principle)
1) 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
2) 다형성을 잘 활용해서 지킬 수 있다.
3) 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현할 수 있다.
4) OCP 원칙을 잘 지키는 개발을 하려면 객체를 생성하고, 연관관계를 맺어주는 별도의 설정자가 필요하다.
-> 해당 역할을 Spring 이 수행한다.
LSP (Liskov Substitution Principle)
1) 프로그램 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
2) 단순하게 컴파일에 성공하는것을 말하는 원칙이 아님.
3) 만약에, 드라이기 버튼을 눌렀을때, 바람이 나와야지 바람이 빨려 들어가면 LSP 설계 위반이다.
ISP (Interface Segregation Principle)
1) 특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다.
2) 쇼핑몰 인터페이스 -> 로그인 인터페이스, 주문 인터페이스처럼 분리하는 게 낫다.
-> 로그인을 바꾸기 위해 주문 인터페이스 코드를 바꿀 필요가 없어진다.
3) 인터페이스를 잘 분리한다면, 인터페이스가 통합되어있을 때보다 명확해지고 대체 가능성도 높아진다.
DIP (Dependency Inversion Principle)
1) 추상화에 의존해야만하고, 구체화에 의존하면 안 된다. -> 역할(Role)에 의존해야지 구현에 의존하면 안 된다.
2) 클라이언트 코드가 구현클래스에 의존하지 말고, 인터페이스에 의존해야 된다는 의미가 된다.
3) 추상화에 의존하면 유연하게 구현체를 변경할 수 있지만,
구현체에 의존하게 되면 변경 자체가 매우 어려워진다.
4) 운전자가 자동차의 역할에 대해서 알아야지 특정 자동차 모델에 대해서만 안다면 안된다.
5개의 원칙 중에 DIP 원칙은 특히나 중요하다.
아래의 그림을 보자.
간단하게 UserService 객체는 User의 정보를 가져와서 특정 서비스를 구현하는 객체이다.
UserRepository 인터페이스는 유저들의 정보를 특정 디비에서
가져올 수 있도록 해주는 구현을 담당하고 있다.
그리고 해당 인터페이스를 직접 구현하는 MySqlRepos, RedisRepos 가 존재한다.
(각각 회원의 정보를 MySql, Redis에서 가져온다는 뜻)
public class UserService {
//private UserRepository userRepository = new MySqlRepos();
private UserRepository userRepository = new RedisRepos();
}
코드를 봤을 때는 해당 객체가 인터페이스에만 의존한 것처럼 보인다.
그래서 아래와 같은 관계를 도식화할 수 있다.
위의 그림과 같이 UserService 객체가 인터페이스를 의존한 것처럼 보이니까
DIP를 잘 적용했다고 생각하면 안 된다.
사실은 아래의 그림이 위 코드 상황에 더 맞는 그림이다.
즉, 위의 코드에서 UserService 객체는 추상화에도 의존하고 있고, 구현부에도 의존하고 있으므로,
DIP 원칙이 지켜지지 않았다고 볼 수 있다.