이전 포스팅에서 SOLID 원칙에 대해서 알아보았습니다. 이번에는 좀 더 구체적으로 하나씩 이해해보려고 합니다. 첫 번째로 단일 책임 원칙에 대해서 예제를 통해 알아보도록 하겠습니다.
2023.01.14 - [개발/개발 공통] - [객체 지향] SOLID 원칙에 대해서 알아보자
단일 책임 원칙(SRP)의 이해와 예제
단일 책임 원칙 (SRP) 란 무엇인가
단일 책임 원칙 (Single Responsibility Principle) 은 하나의 클래스는 하나의 책임만 가져야 한다는 내용이다.
이 내용은 객체지향 뿐만 아니라 프로그래밍 전반에 적용될 수 있는 원칙이라고 할 수 있다. 결국 프로그래밍과 코드는 사람이 이해하고 유지관리하는 것이기 때문에 하나의 클래스, 하나의 모듈이 한 문장으로 설명될 수 있어야 올바른 방향의 코드라고 할 수 있다고 생각한다.
객체지향에서는 일반적으로 클래스 단위로 이해하면 좋을 것 같다. 클래스는 하나의 책임을 가져야 하고 캡슐화해야 한다. 또한 수정이 발생한다면 그 이유는 하나여야 한다. 즉 여러 가지 이유로 인해 해당 클래스가 계속 수정되어야 한다면 그 클래스는 SRP를 위반하고 있다고 판단할 수 있다.
SRP를 적용해서 리팩토링을 하기 위해서는 하나의 클래스를 추가로 생성해서 권한을 위임하거나 분리하는 방법이 있다. 이 때 기존 클래스와 새로 생성된 클래스 간에 가능한 의존성이 적어서 복잡도가 낮아야 한다.
만약 추출된 클래스가 여러개가 있는데 공통된 부분이 있다면 Super 클래스를 생성하는 게 중복 코드를 방지하고 단일 책임을 갖고 응집도를 높이는데 좋을 것이다.
예제
우선 아래 Car 클래스를 보도록하겠습니다.
자동차의 연료를 초기화하고 남아있는 연료를 확인하고 채우는 예제입니다.
여기서 Car 클래스의 책임과 역할에 대해 생각해 볼 필요가 있습니다. 상황에 따라 다를 수 있지만 아래의 경우 조금 어색한 메쏘드 중 하나가 바로 refill입니다. 다른 항목들은 현재 연료의 상태를 확인하거나, run 하였을 때 연료가 줄어드는 역할을 하는데 반해 refill 은 외부에서 주입하여 초기화하는 역할입니다.
class Car {
private:
int max_fuel;
int remain_fuel;
public:
Car(int fuel) {
max_fuel = fuel;
remain_fuel = fuel;
}
void setRemainFuel(int fuel) {
remain_fuel = fuel;
}
int getRemainFuel() {
return remain_fuel;
}
int getMaxFuel()
{
return max_fuel;
}
void run() {
setRemainFuel(getRemainFuel()-1);
}
void refill() {
remain_fuel = max_fuel;
}
};
따라서 저는 refill과 같은 역할은 Car 자체에서 할 것이 아니라 다른 곳에서 책임져야 한다고 생각합니다.
그래서 저는 GasStation이라는 주유소 클래스를 생성하고 그곳에서 refill을 책임지도록 코드를 수정해 보았습니다.
class Car {
private:
int max_fuel;
int remain_fuel;
public:
Car(int fuel) {
max_fuel = fuel;
remain_fuel = fuel;
}
void setRemainFuel(int fuel) {
remain_fuel = fuel;
}
int getRemainFuel() {
return remain_fuel;
}
int getMaxFuel()
{
return max_fuel;
}
void run() {
setRemainFuel(getRemainFuel()-1);
}
};
class GasStation
{
public:
void refill(Car* car)
{
car->setRemainFuel(car->getMaxFuel());
}
};
그리고 아래와 같이 GoogleTest를 구현해서 검증해 보았습니다.
우선 car를 생성하고 run 한 뒤 최초 MAX 값에서 ACC_CNT 만큼 감소했는지 확인합니다. 이는 수정하기 전에도 적용해 볼 수 있는 기본 테스트입니다.
이후 station->refill(car)를 통해 해당 car를 주유소에서 refill 하도록 하였고 현재 남은 연료가 최초 MAX의 값과 동일한지 확인하도록 구현해 보았습니다.
TEST(Car, CarTest)
{
const int MAX = 110;
const int ACC_CNT = 10;
Car* car = new Car(MAX);
GasStation* station = new GasStation();
for (int i = 0; i < ACC_CNT; i++)
car->run();
EXPECT_EQ(car->getRemainFuel(), MAX-ACC_CNT);
station->refill(car);
EXPECT_EQ(car->getRemainFuel(), MAX);
}
결론
제가 생각했을 때 SOLID 원칙 중에 가장 기본적이면서 중요한 내용이 SRP라고 생각합니다. 코드의 가독성을 높이고 복잡도를 낮춤에 있어서 SRP는 매우 중요하고 항상 염두에 두어야 할 원칙입니다.
'개발 > 개발 공통' 카테고리의 다른 글
[객체지향 SOLID 원칙] 리스코프 치환 원칙(LSP)의 이해와 예제 (0) | 2023.05.26 |
---|---|
[객체지향 SOLID 원칙] 개방 폐쇄 원칙(OCP)의 이해와 예제 (0) | 2023.05.26 |
좋은 코드 개발 문화 (클린 코드, 코드 리뷰, TDD) (0) | 2023.05.22 |
[ChatGPT] 챗GPT 가입, 사용법 및 활용 팁 정리 (0) | 2023.03.01 |
[개발 방법] 테스트 주도 개발(Test-Driven Development)에 대해서 (0) | 2023.02.21 |