개방-패쇄 원칙

  • Open / Close Principle
  • 소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야 한다.

 

구현 클래스를 직접 사용하는 것은 변경이 번거롭다

@Service
@RequiredArgsConstructor
public class PaymentService {

    private final PaymentRepository paymentRepository;
//    private final FixDiscountPolicy fixDiscountPolicy;    - 할인 정책 변경으로 인한 미사용
    private final PercentDiscountPolicy percentDiscountPolicy;

    public void addHistory (long amount) {
//        amount = fixDiscountPolicy.apply(amount);      - 할인 정책 변경으로 인한 미사용
        
        // 할인 정책 변경 적용
        amount = percentDiscountPolicy.apply(amount);

        // 결제 내역 저장
        paymentRepository.save(amount);
    }
}


public class FixDiscountPolicy {

    public long apply(long amount) {
        return amount - 5000L;
    }

}

public class PercentDiscountPolicy {

    public long apply(long amount) {
        return (long) (amount * 0.9);
    }
}

  • 위의 코드의 PaymentService 에서 사용하는 할인 정책을 변경하려면 기존 할인 정책을 사용한 코드를 변경해야한다. 또한, 같은 할인 정책을 사용하는 모듈이 여러개 존재할 경우 모두 찾아서 변경해야한다. 그뿐만 아니라 할인정책이 변경되었는데 결제를 담당하는 PaymentService 의 코드를 변경해야된다는 점에서 단일 책임 원칙에도 위배되고 있다.

 

인터페이스를 활용하자

@Service
@RequiredArgsConstructor
public class PaymentService {

    private final PaymentRepository paymentRepository;
    private final DiscountPolicy DiscountPolicy;

    public void addHistory (long amount) {
        amount = DiscountPolicy.apply(amount);

        // 결제 내역 저장
        paymentRepository.save(amount);
    }
}


public interface DiscountPolicy {

    long apply(long amount);
}


public class FixDiscountPolicy implements DiscountPolicy{

    public long apply(long amount) {
        if (amount >= 100000) {
            return (long) (amount * 0.9);
        } else if (amount >= 50000) {
            return (long) (amount * 0.8);
        }
        return amount;
    }

}

public class PercentDiscountPolicy implements DiscountPolicy{

    public long apply(long amount) {
        return (long) (amount * 0.9);
    }
}

  • 기존에 할인 정책의 구현체를 직접 의존하던 것을 인터페이스를 통해 의존하도록 변경하였다. 이를 통해 새로운 정책이 필요할 때에는 인터페이스를 구현한 새로운 구현체를 만들어 확장할 수 있고 이를 적용할 때에도 기존에 코드를 변경하지 않고 적용할 수 있다. 즉, 기존 코드를 유지한채로 기능을 확장할 수 있다. 다만, 외부에서 구현체를 생성하고 연관관계를 맺어주는 별도의 모듈이 필요하며 스프링의 IoC 컨테이너 등이 이에 해당한다.

 

다음글

+ Recent posts