본문 바로가기
디자인 패턴

2-1. SOLID(S.O.L.I.D.)

by spaul 2023. 10. 16.

▣ What is "SOLID"?

SOLID(S.O.L.I.D.)는 미국의 프로그래머 Robert C. Martin이 2000년에 발표한 논문 "Design Principles and Design Patterns")에서 발표한 5가지의 소프트웨어 설계 원칙입니다. 이해하기 쉽고, 유연하며, 유지 보수가 쉬운 소프트웨어 개발을 위한 원칙들이죠.

 

그 5가지의 원칙은 아래와 같습니다.

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

- OCP : Open-Closed Principle (개방-폐쇄 원칙)

- LSP : Liskov Substituion Principle (리스코프 치환 원칙)

- ISP : Interface Segregation Principle (인터페이스 분리 원칙)

- DIP : Dependency Inversion Principle (의존 역전 원칙)

 

용어만 봐서는 무슨 뜻인지 모르는게 당연합니다. 지금부터 원칙들에 대해 하나하나 설명해보도록 하겠습니다.

▣ SRP (Single Responsibility Principle)

Gather together those things that change for the same reason, and separate those things that change for different reasons.

- Robert C.Martin -

 

위에서 말씀 드렸듯이 SOLID 원칙을 발표한 미국의 프로그래머 Robert C.Martin이 직접 SRP에 대해서 정의한 내용입니다. 해석해보면, "같은 이유로 변경되는 것들을 하나로 모으고, 다른 이유로 변경되는 것들을 분리시킨다"라고 합니다. 정의를 보니 더 헷갈리는 것 같네요. 좀 더 구체적으로 SRP에 대해 정리해보면 아래와 같습니다.

■ 클래스가 변경되어야 하는 이유는 한 가지로만 구성

■ 클래스의 역할을 한 가지로 구성

■ 사용자와의 관계에 대해서 고민  

 

위의 내용들을 토대로 SRP에 대해 한 마디로 정의해보자면, 하나의 클래스는 하나의 작업만을 수행해야 된다는 것입니다. 이유는 짐작하시다시피, 하나의 클래스에 너무 많은 기능을 추가하면 유지 보수가 어려워지기 때문입니다. 

 

위의 클래스 다이어그램과 같은 메서드와 멤버 변수를 가지는 Book이라는 클래스가 있습니다. 이 Book이라는 클래스의 load() 메서드는 파일에서 Book 정보를 읽어서 멤버 변수들에 저장하고, show() 메서드는 콘솔 화면에 해당 객체의 정보를 보이는 역할을 합니다.  

 

여기서 우리가 책의 정보를 관리하는 BookManager 클래스를 추가하고, BookManager를 통해 책의 정보들을 관리한다고 가정해봅시다. 만약 프로그램의 수정을 여기서 멈춘다면, 이 프로그램은 SRP를 지키는 프로그램이 될 것입니다. 하나의 클래스가 하나의 역할만 수행하도록 잘 설계됐다고 볼 수 있기 때문입니다.

 

 (클래스 다이어그램에서 각 화살표에 대한 의미는 아래 링크에 잘 정리되어 있으니 한 번씩 읽어보시길 추천드립니다
https://www.nextree.co.kr/p6753/)

 

그러나 글의 제일 상단에서 S.O.L.I.D는 유지보수가 쉬운 소프트웨어 개발을 위한 원칙들이라고 언급한 바 있습니다. 우리는 유지보수가 어떤 의미인지 한 번 생각해 볼 필요가 있는데, 프로그램을 사용하다보면 이것 저것 다른 기능들을 추가하고 싶어지기 마련이고, 같은 기능이라도 더 편리하고 사용자 친화적인 인터페이스로 구현하고 싶어질 것입니다. 이것이 소프트웨어의 유지보수라고 할 수 있습니다. 

 

위의 BookManager와 Book 클래스에서 변경되는 부분이 생기게 된다면 어떨까요? 파일이 아니라 DB에서 책 데이터를 읽어서 저장하는 load() 함수를 만들어야 할 수도 있고, 콘솔 화면이 아닌 GUI를 통해 화면에 책 내용을 출력하는 show() 함수를 만들어야 할 수도 있습니다. 이처럼 변경되는 부분이 생길 때마다, 우리는 BookManager 클래스의 메서드들을 직접 수정하거나 추가해야할까요?

 

 

이러한 상황을 최대한 방지하기 위해, BookManager 클래스의 load()와 show()는 그대로 두고, BookDataLoader와 BookDataViewer라는 클래스를 따로 생성하여 BookManager에서 사용하도록 만들 수 있습니다. 이렇게 클래스를 설계하면 BookManager 클래스의 변경은 최소화 하면서, 새로운 기능 추가나 수정이 필요하다면 BookManager 클래스와 집합관계에 있는 BookDataLoader 클래스와 BookDataViewer 클래스만 수정하여 기존과 같이 사용할 수 있게 됩니다.

 

▣ OCP (Open-Closed Principle)

   A module should be open for extension but closed for modification

- Robert C.Martin -

 

OCP는 하나의 모듈은 확장에는 열려있어야하고 변경에는 닫혀 있어야 한다는 원칙입니다. 변경이란 기존 클래스의 코드를 수정하는 것을 뜻하고, 확장은 클래스에 새로운 기능을 추가하는 것입니다. 즉, 기존 코드를 최대한 변경하지 않으면서, 확장할 수 있도록 만들어야한다는 뜻입니다.

 

기존 코드를 최대한 변경하지 않는다는 점에서 위에서 살펴봤던 SRP와 비슷한 점이 있군요. 사실 S.O.L.I.D의 각각의 원칙들은 서로 중복되는 부분들이 많습니다. 기존 코드를 변경하지 않으면서 확장할 수 있게 하려면, 변경되지 않을 부분(닫힌 부분)과 변경 될 부분(열린 부분)이 필요할 것 같네요. 열린 부분을 구현하기 위해서 제공되는 것이 인터페이스(interface)입니다.

 

예를 들어 우리가 어떤 건물의 문을 만드는데, 처음에 건물에 들어가는 문이 자동문, 손잡이문, 미닫이문 3개만 있었다고 해봅시다. 그러면 우리는 문을 열 때 아래와 같은 로직으로 문을 여는 것을 추상화 해볼 수 있습니다. 

if (door instanceof AutomaticDoor)
        client.pressOpen(door);
else if (door instanceof KnobDoor)
        client.twistOpen(door);
else if (door instanceof SlidingDoor)
        client.slideOpen(door);

 

그런데 만약 위의 세 가지 종류의 문이 아닌, 다른 종류의 문(예를 들면 회전문)이 하나 추가되었다고 생각해봅시다. 그러면 우리는 기존 코드에서 else if 문을 하나 더 추가해서 새로운 종류의 문에 대한 코드를 작성해야 할 것입니다. 

 

지금은 문으로 예시를 들었고, 문의 종류는 한정적이므로 기껏해봐야 몇 줄 추가하는게 더 쉬울 수 있습니다. 그러나 우리가 실제로 사용하는 소프트웨어에서 이런 일이 발생한다면, 기존의 코드를 수정하고 추가하는 것들이 굉장히 번거롭고 어려운 일이 될 수 있습니다. 그래서 우리는 기능의 추가를 좀 더 쉽게 하기 위해서, 아래의 그림과 같이 다형성이라는 기능을 사용하여 이런 일을 방지합니다.

 

이 포스팅에서는 다형성이 무엇인지와 같은 자바 기능에 대해서 설명드리진 않겠습니다. 어쨌든 이런 구조를 통해서 기존 Door 클래스의 수정 없이 Revolving door(회전문) 기능 추가가 이루어질 수 있습니다.

 

포스팅이 너무 길어질 것 같아 나머지 3가지 원칙인 LSP, ISP, DIP는 다음 포스팅에서 다루어보겠습니다.

 

References

[1] 조용주, 고급객체지향프로그래밍

[2] Eric Freeman, Head Frist Design Patterns

'디자인 패턴' 카테고리의 다른 글

5. Singleton Pattern  (0) 2023.11.13
4. Observer Pattern  (0) 2023.11.07
3. Strategy Pattern  (0) 2023.10.29
2-2. SOLID(S.O.L.I.D)  (0) 2023.10.18
1. Software Design Pattern  (0) 2023.10.06