본문 바로가기
프로그래밍

SOLID 디자인 원칙 - 단일 책임 원칙

by bantomak 2023. 11. 15.

SOLID 디자인 원칙

SOLID는 다음과 같은 디자인 원칙들을 아우르는 약어이다.

 

  • 단일 책임 원칙(Single Responsibilty Principle, SRP)
  • 열림-닫힘 원칙(Open-Closed Principle, OCP)
  • 리스코프 치환 원칙(Liskov Substitution Principle, LSP)
  • 인터페이스 분리 원칙(Interface Segregation Principle, ISP)
  • 의존성 역전 원칙(Dependency Inversion Principle, DIP)

 

단일 책임 원칙(Single Responsibility Principle, SRP)

좋은 아이디어가 생각날 때마다 기록해 두는 메모장(메모장은 콩글리시이다. 영어로는 "기록"이라는 의미로 저널이라고 한다. 따라서 아래 예제 코드의 클래스명은 Journal이다.)을 만들기로 했다고 하자. 이 메모장에는 제목 하나에 여러 항목이 저장될 수 있을 것이다. 이러한 기능은 다음과 같이 모델링 될 수 있다.

 

struct Journal
{
   string title;
   vector<string> entries;
   
   explicit Journal(const string& title) : title(title) {}
}

 

이제 메모장의 각 항목이 기입된 순서대로 저장되게 하는 기능을 만들 수 있다.

 

void Journal::add(const string& entry)
{
   static int count = 1;
   entries.push_pack(boost::lexical_cast<string>(count++)) + ": " + entry;
}

 

이제 다음과 같이 메모장을 이용할 수 있다.

 

Journal j{"Dear Diary"};
j.add("I cired today");
j.add("I ate a bug");

 

이 함수가 Journal 클래스에 포함되어 있는 것은 매우 자연스럽고 상식적이다. 왜냐하면 각 항목을 기록할 수 있게 하고 관리할 책임이 메모장에 있기 때문이다. 따라서 그러한 책임을 완수하기 위한 함수라면 Journal에 포함되는 것이 공정하다. 이제 메모장은 영구적으로 파일에 저장하는 기능을 만들기로 했다고 하자.

 

void Journal::save(const string& filename)
{
   ofstream ofs(filename);
   for (auto& s : entries)
   {
      ofs << s << endl;
   }
}

 

이러한 방식은 문제가 있다. 메모장의 책임은 메모 항목들을 기입/관리하는 것이지 디스크에 쓰는 것이 아니다. 만약 디스크에 파일을 쓰는 기능을 데이터의 기입/관리를 담당하는 클래스가 함께 책임지도록 한다면 데이터 저장 방식이 바뀔 때(예를 들어 로컬 디스크 대신 원격 클라우드에 저장)마다 그러한 클래스들을 일일이 모두 수정해야 한다.

 

작은 수정을 여러 클래스에 걸쳐서 해야 한다면 아키텍처에 뭔가 문제가 있다는 징조이다.(이러한 징조들을 통칭하여 보통 "코드 스멜(code smell)" 이라고 부른다.) 물론 상황에 따라 다를 수 있다. 수정할 곳이 수백 군데에서 이르더라도 단순히 심벌 이름을 일괄적으로 바꾸어야 하는 상황이라면 큰 문제가 아니다. 하지만, 인터페이스를 완전히 고쳐야 하는 상황이라면 대단히 고통스러울 것이다.

 

따라서, 이 예에서의 파일 저장 기능은 메모장과 별도로 취급하여 별도의 클래스로 만드는 것이 바람직하다. 예를 들어 다음과 같이 만들 수 있다.

 

struct PersistenceManager
{
   static void save(const Journal& j, const string& filename)
   {
      ofstream ofs(filename);
      for (auto& s : j.entries)
      {
         ofs << s << endl;
      }
   }
}

 

단일 책임 원칙이 의미하는 바가 정확히 바로 이런 것이다. 각 클래스는 단 한가지의 책임을 부여받아, 수정할 이유가 단 한 가지여야 한다. Journal 클래스는 기록할 항목에 대해 뭔가 바꿀 것이 있을 때 코드 수정이 되어야 한다. 예를 들어 각 항목이 날짜와 시간을 접두어 가지게 해야 한다면 add() 함수를 그렇게 하도록 수정해야 한다. 반면에 영구적인 저장 방식을 바꾸어야 한다면 PersistenceManager를 수정해야 한다.

 

SRP를 위배하는 안티 패턴의 극단적인 예로 전지전능한 객체가 있다. 전지전능 객체는 가능한 많은 기능을 담아 하나의 괴물 같은 클래스를 이룬다. 이러한 객체와 함꼐 일하는 것은 대단히 어렵다.

 

다행히도, 이러한 객체는 소스 관리 시스템에 의해 쉽게 식별될 수 있다. (멤버 함수의 개수만 헤어리면 된다.)

전지전능 객체를 만드는 개발자들은 가능한 빨리 식별되어 바로 잡도록 가이드되어야 한다.

 

출처

 

모던 C++ 디자인 패턴 - 예스24

새로운 기능으로 풍부해진 C++로 다시 배운다. C++는 C++11/14/17을 거치면서 강력한 언어로 발전했으며, 표현력이 풍부해졌다. GoF의 전통적인 디자인 패턴을 표현력이 풍부해진 모던 C++로 새롭게 학

www.yes24.com

댓글