개인공부

[ 코드 설계 ] 단일 책임 원칙 SRP

KEEMSY 2023. 9. 26. 19:38

단일 책임 원칙이란, 클래스와 모듈은 하나의 책임 또는 기능만을 가지고 있어야 한다는 설계원칙이다. 즉, 거대하고 포괄적인 클래스를 설계하는 대신, 작은 단위와 단일 기능을 가진 클래스를 설계해야 한다는 것이다.

  • "변경해야 할 이유는 한가지이어야한다." 으로도 이야기 할 수 있다.
  • 클래스에 비즈니스와 관련 없는 기능이 두 개 이상 포함되어 있으면 책임이 단일하지 않으므로, 단일 기능을 가진 여러개의 작은 클래스로 분할 되어야 한다.

 

하지만 단일 책임 원칙이 설명하는 대상에는 클래스와 모듈이 라는 것에 주의를 해야한다. 이는 다르게 해석이 될 수 있기 때문이다

  • 해석1: 모듈을 클래스보다 더 추상적인 개념으로 간주하고 클래스를 일종의 모듈로 간주한다.
  • 해석2: 모듈을 좀 더 포괄적인 대상으로 놓고, 여러 클래스가 하나의 모듈을 구성한다고 간주한다.
  • 어떤 방식으로 이해하든, 클래스와 모듈에 적용할 때는 서로 의미가 동일하다.

 


클래스에 단일 책임이 있는지 판단하는 방법

 

실제 소프트웨어 개발에서 클래스에 단일 책임이 있는지 판별하는 것은 쉬운 일이 아니다. 이를 설명하기 위해 UserInfo 를 예제를 사용한다.

public class UserInfo {

    private long userId;

    private String username;

    private String email;

    private String telephone;

    private long createTime;

    private long lastLoginTime;

    private String avatarUrl;

    private String provinceOfAddress; // 도

    private String citoOfAdress; // 시

    private String regionOfAddress; // 구

    private String detailedAddress; // 상세 주소

    // 일부 속성과 메서드 생략

}


이 클래스는 단일 책임 원칙을 충족할까? 에 대한 질문에는 2가지 다른 견해가 존재할 수 있다.

  • 견해1. 단일 책임 원칙을 준수한다: UserInfo 클래스에는 사용자와 관련된 정보가 포함되어 있고 모든 속성과 메서드가 사용자와 같은 비즈니스 모델에 속해 있어 단일 책임 원칙을 준수한다.
  • 견해2. 단일 책임 원칙을 준수하지 않는다: UserInfo 클래스에서 주소 정보의 비율이 상대적으로 높기 때문에 주소정보를 독립적인 UserAddress 클래스로 분할 해야한다. 즉 UserInfo 주소 정보를 제외한 다른 정보만 보유하도록 하여 두 클래스의 책임을 단일화 해야한다.

 

두 견해 모두 타당성이 있어보인다. 그렇다면 어느 견해가 더 타당할까? 이는 시나리오를 살펴볼 필요가 있다.

시나리오 1. 견해 1 이 타당한경우

(소셜 네트워크 제품와 같은 상황)`사용자의 주소 정보`가 다른 사용자 정보와 동일하게 정보 표시에만 사용되며, 다른 정보와 함께 사용된다.
시나리오 2. 견해 2 이 타당한 경우

(소셜 네트워크 상품이 발전) 전자 상거래 기능 모듈이 제품에 추가되어, 사용자의 주소 정보는 표시를 위해 사용될 뿐만 아니라 전자 상거래의 물류에 독립적으로 적용되는 경우

이러한 시나리오를 살펴보고 알 수 있듯이, 동일한 클래스라 할지라도 다른 응용 시나리오나 다른 단계의 요구 사항에 따라 클래스의 책임이 단일한지 아닌지를 판단하는 것이 다를 수 있다.

 

  • 응용 시나리오를 변경하거나 요구 사항이 이후 달라질 경우에는 해당 클래스의 설계가 단일 책임 원칙을 충족하지 못할 수 있으므로 계속해서 더 작은 클래스로 분할해야 한다.

 

이 밖에도 다른 비즈니스 수준에서 동일한 클래스의 설계를 보는 관점에 따라 해당 클래스에 단일 책임이 있는지를 판단하는 결과가 다를 수 있다.

  • 사용자 비즈니스 수준의 관점에서는 모두 사용자의 정보이므로 단일 책임 원칙이 충족된다.
  • (사용자 표시 정보, 주소 정보, 로그인 인증 정보와 같은) 더 세분화된 비즈니스 수준에서는 단일 책임 원칙을 충족하지 않으며 계속 분할애햐 하는 대상이 된다.

정리를 한다면, 클래스에 단일 책임이 있는지를 평가하기 위한 명확하고 정량화 할 수 있는 표준은 존재하지 않는다.

  • 소프트웨어를 개발할 때는 과도하게 너무 세분화하여 설계할 필요는 없다.
  • 사업이 발전하면서 클래스에 기능이 점점 추가되고, 코드가 점점 더 복잡해지면서 거대해진 클래스를 여러 개의 세분화된 클래스로 나눌 수 있을 것이다.

단일 책임 여부를 결정하기 위해 사용되는 몇가지 결정 원칙들

 

1. 클래스에 코드, 함수 또는 속성이 너무 많아 코드의 가독성과 유지 보수성에 영향을 미치는 경우, 클래스 분할을 고려해야 한다.

2. 클래스가 너무 과하게 다른 클래스에 의존한다면(높은 결합도), 높은 응집도와 낮은 결합도의 코드 설계에 적합하지 않음을 의미 하므로 클래스 분할을 고려해야한다.

3. 클래스에 private 메서드가 너무 많은 경우 이 private 메서드를 새로운 클래스로 분리하고, 더 많은 클래스에서 사용할 수 있도록 public 메서드로 설정하여 코드의 재사용성을 높인다.

4. 클래스의 이름을 비즈니스적으로 정확하게 지정하기 어렵거나 Manager, Context 처럼 일반적인 단어가 아니면 클래스의 이름을 정의하기 어려울 경우, 클래스 책임 정의가 충분히 명확하지 않음을 의미할 수 있다.

5. UserInfo 클래스의 많은 메서드가 주소를 위해서만 구현된 앞의 예시처럼 클래스의 많은 메서드가 여러 속성 중 일부에서만 작동하는 경우 이러한 속성과 해당 메서드를 분할하는 것을 고려할 수 있다.

 

 

 

참고자료: 디자인 패턴의 아름다움

728x90