이 글은 객체지향 5대 원칙, SOLID principle 중 S인 Single Responsibility Principle 단일 책임 원칙에 대해 알아본다.
정의
Single Responsibility Principle은 모든 class는 단 하나의 책임을 가져야 한다를 의미한다. 여기서 책임이란 단어가 조금 애매한데, 해당 class가 수행하는 역할이라고 이해해도 된다. 따라서 class가 단 하나의 역할만 가져야 한다고 이해해도 무방하며, 즉슨 class 변경의 이유가 맡고 있는 역할에 대한 변경 단 하나뿐이어야 한다는 것도 의미한다.
예를 들어 class teamA, class teamB가 Data class의 getData()라는 method를 사용한다고 가정하자. getData() method는 Data class가 가지고 있는 값을 string으로 리턴하는 method이다. 이 때 Data class는 Single Responsibility Principle을 만족하는가?
아니다. getData() method는 [teamA에게 주는 data / teamB에게 주는 data] 이렇게 2개의 책임(역할)을 가지고 있다. 만약 teamA가 요청하는 데이터 형식 string에서 byte[]로 바뀐다면? getData() method는 변경되어야 하고, teamB에게 주는 data가 맞는지도 검증해야 한다.
이렇듯 class가 여러 개의 책임(역할)을 가지게 된다면 많은 역할을 가지고 있기 때문에 해당 class와 연관된 class들이 많아진다. 따라서 어떤 기능을 수정하면 연관된 모든 class가 정상적으로 작동하는지 확인해야 한다. 프로그램을 유지보수함에 있어 확인하고 신경써야 하는 것이 많아지는 것이다. Single Responsibility Principle은 이러한 상황을 막기 위한 원칙이다.
어떻게 사용해야 할까?
Single Responsibility Principle의 핵심은 class의 역할을 최대한 단순하게 만드는 것이며 이를 위해 적절한 class의 분리가 필요하다.
적용된 예시로 Facade pattern과 object composition가 있으니 이들을 참고하자.
* Facade Pattern
각 기능에 대한 책임을 지는 class를 분리하고, 이들을 통합으로 사용하는 Facade class를 하나 만든다. Facade class는 분리한 class를 생성하고 method를 실행하는 역할만 한다.(object composition)
이렇게 코드를 짜면 해당 system의 actor는 system을 직접 조작하는 것이 아니라 Facade class를 조작함으로써 system을 조작할 수 있다. 따라서 Facade class는 system 조작에 대한 책임만 있는 것이고 system의 각 기능에 대한 subsystem은 subsystem에 대한 책임만 있는 것이기 때문에 Single Responsibilitiy Principle을 만족하는 코드를 작성할 수 있다.
언제 사용해야 할까?
class가 2개 이상의 책임을 가질 때
Single Responsibility Principle에 의해 분리해야 한다. 이 건의 원칙의 정의에 의해 이루어져야 하는 것이다.
class를 사용하는 class들이(actor가) 다르지만, 같은 method를 사용할 때
actor가 다르다면 해당 method의 변경 요청을 다르게 할 수 있으므로 분리해야 한다.
이점
유지보수성 향상
class가 단 하나의 기능을 가지고 있기 때문에 기능을 수정해야 할 경우 해당 class의 기능만 찾아 고치면 되며 다른 class는 수정하지 않아도 된다.
코드 변경으로 인한 effect를 줄일 수 있다.
class가 단 하나의 기능을 가지고 있다면 해당 기능을 수정하더라도 연관된 class가 적기 때문에 effect가 적다.
테스트 용이성
기능이 작기 때문에 각 class를 독립적으로 테스트 할 수 있으며 unit test가 쉬워진다.
유의점
Single Responsibility Principle은 간단한 원칙이지만 지키기 어렵다. class에 얼만큼의 책임을 넣어야 할지 그 기준이 명확하게 정해져 있지 않기 때문이다. 또한 유지보수를 하다 보면 잘 지켜지던 원칙이 깨질 수도 있는 등 많은 변수가 존재한다.
Single Responsibility Principle이 적용된 다양한 예시들을 보고 항상 더 좋은 방법이 없는지 끊임없이 고민하며 코드를 작성해야 한다.
아래는 ChatGPT가 생성한 예시 코드이다.
class User {
private:
string username;
string password;
public:
void setUsername(string name) {
username = name;
}
void setPassword(string pass) {
password = pass;
}
string getUsername() const {
return username;
}
bool checkPassword(string pass) const {
return password == pass;
}
};
class Authenticator {
public:
bool authenticate(const User& user, string password) const {
return user.checkPassword(password);
}
};
class SessionManager {
public:
void startSession(const User& user) {
// Start session for the user
}
void endSession(const User& user) {
// End session for the user
}
};
User class는 사용자 정보만 관리한다. 인증 방식이나 세션 관리 방식이 바뀌어도 User는 수정하지 않아도 된다. SRP를 잘 만족한다.
Authenticator class는 인증만 관리한다. 위 코드에서는 입력받은 string과 같은지 여부만 비교하지만 실제 코드를 작성한다면 암호화를 담당하는 PasswordEncryptor를 하나 더 만들고 입력받은 password를 encrypt할 것이다. 만약 인증 방식이 바뀌어도 Authenticator만 수정하면 된다. User를 수정하지 않아도 된다. SRP를 잘 만족한다.
SessionManager class는 session에만 책임이 있다. session 작동 방식이 바뀌는 경우 SessionManager만 수정하면 된다. User를 수정하지 않아도 된다. SRP를 잘 만족한다.
'CS > OOP' 카테고리의 다른 글
[OOP] SOLID - Dependency Inversion Principle (0) | 2023.03.12 |
---|---|
[OOP] SOLID - Interface Segregation Principle (0) | 2023.03.11 |
[OOP] SOLID - Liskov Substitution Principle (0) | 2023.03.09 |
[OOP] SOLID - Open Closed Principle (0) | 2023.03.07 |
[OOP] 객체지향 프로그래밍 Object-Oriented Programming (0) | 2023.02.17 |