(2-1) SOLID(객체 지향 설계): 단일 책임의 원칙(SRP)

728x90
SOLID 란 프로그래머가 시간이 지나도 유지 보수와 확장이 쉬운 시스템을 만들고자 할 때 이 원칙들을 함께 적용할 수 있다. SOLID 원칙들은 소프트웨어 작업에서 프로그래머가 소스 코드가 읽기 쉽고 확장하기 쉽게 될 때 까지 소프트웨어 소스 코드를 리팩터링하여 코드 냄새를 제거하기 위해 적용할 수 있는 지침이다.

단일 책임의 원칙(SRP): Single Responsibillity Principle

단일 책임의 원칙의 키워드는 다음과 같다.

  • 클래스는 단 한 개의 책임을 가져야 한다.
  • 클래스의 변경하는 이유는 단 한개여야 한다.
  • 누가 해당 메소드의 변경을 유발하는 사용자인가?

단일 책임의 원칙이라는 것은 이해하기 난해하다. 명확한 책임을 도출하기까지 시간이 걸리기 때문에 처음부터 단일 책임을 지켜서 설계하는 것은 힘들다. 또 요구사항이 변경 될 경우 책임 또한 변경되게 된다. 그렇기에 지속해서 한 개의 클래스가 하나의 책임을 갖게 하기는 어렵다.

객체 지향 특성 중, 단일 책임 원칙과 가장 관계가 깊은 특성은 모델링을 담당하는 '추상화' 이다.


요구사항

  • 카드 결제 시스템이 존재
  • 현재 국내 결제를 지원하는 카드는 신한, 우리 카드가 존재
  • 국내 결제 카드사들은 지속해서 추가
  • 미래에 해외 결제 기능이 추가
    • 신한 카드는 해외 결제가 가능
    • 우리 카드는 해외 결제가 불가능
    • 지속해서 카드사가 추가

 

기존 국내 카드 결제 서비스

public interface CardPaymentService {
    void pay(CardPaymentDto.PaymentRequest req);
}

public class ShinhanCardPaymentService implements CardPaymentService {
    @Override
    public void pay(CardPaymentDto.PaymentRequest req) {
        // .. 신한 카드 국내 결제 로직..
        shinhanCardApi.pay(paymentRequest);
    }
}

public class WooriCardPaymentService implements CardPaymentService {
    @Override
    public void pay(CardPaymentDto.PaymentRequest req) {
        // .. 우리 카드 국내 결제 로직..
        wooriCardApi.pay(paymentRequest);
    }
}

 

추가될 해외 카드 결제 서비스

public interface CardPaymentService {
    void pay(CardPaymentDto.PaymentRequest req);
    void payOverseas(CardPaymentDto.PaymentRequest req);
}

public class ShinhanCardPaymentService implements CardPaymentService {
    @Override
    public void payOverseas(CardPaymentDto.PaymentRequest req) {
        // .. 신한 카드 해외 결제 로직..
        shinhanCardApi.pay(paymentRequest);
    }
}

public class WooriCardPaymentService implements CardPaymentService {
    @Override
    public void payOverseas(CardPaymentDto.PaymentRequest req) {
        // 우리 카드 결제는 해외 결제 기능이 없음...
    }
}

 

신한 카드는 해외 결제를 할 수 있지만 우리 카드는 해외 결제 기능을 제공하지 않고 있다. 각 구현 클래스들은 CardPaymentService 인터페이스를 구현하고 있으므로 payOverseas 기능이 추가되면 우리 카드 결제는 반드시 해당 메서드를 구현해야 한다.

해외 결제만 되고 국내 결제가 안되는 카드사가 추가될 경우 위와 반대로 payOverseas 는 구현하고 pay 는 구현하지 못하게 된다.

SRP 에서 책임이란 변화에 대한 것이다.

국내 결제에서 해외 결제라는 책임이 하나 더 생긴 것이다. 그렇게 두 개의 책임이 생겼기에 클래스의 책임을 나누는 작업이 필요해졌다.

만약, 우리 카드가 해외 결제를 제공하고 다른 카드사들도 해외 결제를 제공한다면 국내, 해외 결제를 할 수 있고, 하나의 책임을 가질 수 있다고 볼 수 있으며 단일 책임의 원칙을 지켰다고 볼 수도 있다.

하지만 미래에 어떻게 될 것인지를 예측할 수 없기 때문에 단일 책임의 원칙을 지키는 것은 매우 어려운 일이다.

 

해외 결제 서비스와 국내 결제 서비스에 대한 두 개의 인터페이스를 만들어서 국내 결제와 해외 결제에 대한 책임을 분리했고 하나의 클래스가 하나의 책임을 갖도록 변경했다. 새로운 카드사 추가 시에 유연하게 확장을 할 수 있도록 할 수 있다.

즉, 단일 책임 원칙(SRP) 와 가장 관계가 깊은 특성은 '추상화' 라고 할 수 있다.


참조

728x90