예를 들어 다음과 같은 post 클래스가 있다고 가정한다.
class Post {
// 글 제목
getTitle() { ... }
// 글 내용
getDesc() { ... }
// DB에서 글 가져오기
getPost() { ... }
// DB에서 글 삭제하기
deletePost() { ... }
}
Post 클래스는 Post Entity와 Post DB에 대한 두 접근이 가능하므로 2개의 책임을 갖고 있다.
그렇기에 SRP 원칙을 위반했고 이는 다음과 같이 고쳐 SRP 원칙을 지킬 수 있다.
class Post{
// 글 제목
getTitle() { ... }
// 글 내용
getDesc() { ... }
}
class PostDb{
// DB에서 글 가져오기
getPost() { ... }
// DB에서 글 삭제하기
deletePost() { ... }
}
public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
OrderServiceImple 클래스는 할인 정책을 담당하는 DiscountPolicy 인터페이스를 의존한다.
할인 정책이 변경될 때 DiscountPolicy의 구현체를 변경해야 됨에 따라 OrderServicImpl 클래스의 변경도 일어난다.
즉, 클라이언트 코드도 함께 변경해야 하고 이는 OCP 원칙 위반이다.
public class OrderServiceImpl implements OrderService {
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
public class AppConfig {
public OrderService orderService() {
return new OrderServiceImpl(new FixDiscountPolicy());
}
}
위 코드는 AppConfig 클래스를 통해 인터페이스 DiscountPolicy의 구현체를 주입 받는다.
이렇게 되면 할인 정책이 변경될 때, OrderServiceImpl에 직접적인 수정을 하지 않고 AppCofig 클래스만 변경하면 돼서 확장은 열려 있고 변경은 닫힌 OCP 원칙을 지킬 수 있게 된다.
예를 들어 자동차 인터페이스의 엑셀은 앞으로 가라는 기능인데 하위 클래스에서 이를 뒤로 가게 구현한다면 이는 LSP 원칙 위반이다. 속도가 빠르든 느리든 인터페이스 규약에 맞게 전진하도록 구현해야 한다.
public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
앞서 OCP 원칙을 위반한다는 예제 코드다. 사실 위 코드는 OCP 원칙뿐만 아니라 DIP 원칙도 위반하고 있다.
OrderServiceImpl 클래스는 할인 정책 인터페이스 DiscountPolicy의 구현체인 FixDiscountPolicy 또는 Rate DiscountPolicy를 의존하고 있기 때문이다.
public class OrderServiceImpl implements OrderService {
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
public class AppConfig {
public OrderService orderService() {
return new OrderServiceImpl(new FixDiscountPolicy());
}
}
하지만, OCP 원칙을 지킬 수 있도록 고친 위 코드는 OCP뿐만 아니라 추상화에만 의존하기 때문에 DIP 원칙도 지키고 있다.
객체 지향을 공부하고 들어보기만 했던 좋은 객체 지향 설계의 5가지인 SOLID를 공부했다.
아직 용어가 익숙지 않아 헷갈리지만, 다형성이 신이 아님을 확실하게 깨달을 수 있는 시간이었다.
지난 해커톤 때와 현재 진행 중인 졸업작품에서 Spring을 사용하면서 직접적으로 구현체를 의존시켰고 심지어 추상화 작업도 하지 않았다. 지난날들의 안일함을 반성하고 앞으로 SOLID를 지키며 코딩해야겠다.