현재를 살아가는 우리들은 모두 일정한 원리/원칙 아래에서 생활하고 있습니다. 여기서의 원칙 이라 함은 좁은 의미로는 개개인의 사고방식이나 신념, 가치관 정도가 될 수가 있겠고, 넓게는 한 국가의 통치 이념이나 통치 방법 정도가 되겠습니다. 그럼 우리는 왜 이런 원칙들 아래에서 생활하고 있는 걸까요? 이거다라고 단정할 수는 없지만 그건 아마도 그런 원칙들이 개인의 삶을 윤택하게 하고 국민들의 삶의 질을 향상 시키는 데 보다 효율적이고 효과적이기 때문입니다.

마찬가지로 입증된 객체지향 디자인 원리들을 사용하면 좀 더 유지보수하기 쉽고, 유연하고, 확장이 쉬운 소프트웨어를 만들 수 있습니다. 이 원리들은 그 크기를 대비해 보면 패턴보다 훨씬 작지만 표준화 작업에서부터 아키텍처 설계에 이르기까지 다양하게 적용되는 원칙입니다. 초로의 나이이임도 불구하고 태극권의 일인자였던 어느 노인이 이런 말을 했다고 합니다. “나는 한평생을 걸쳐 무술을 연습했지만 이제 서기(자세)를 제대로 할 수 있을 것 같다.” 입문자 때 배우는 서기 자세는 아주 쉬운 것 같지만 역설적이게도 아주 어렵다고 합니다. 우리에게 있어서 객체지향 설계 원칙이 서기(자세) 정도의 의미가 아닐까 생각됩니다. 따라서 이번 시간에는 이런 입증된 원리들에 대해서 알아 봅니다.

1. 5가지 원리의 핵심내용

A. SRP (단일책임의 원칙: Single Responsibility Principle)

THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE.

i. 정의

작성된 클래스는 하나의 기능만 가지며 클래스가 제공하는 모든 서비스는 그 하나의 책임(변화의 축: axis of change)을 수행하는 데 집중되어 있어야 한다는 원칙입니다. 이는 어떤 변화에 의해 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 함을 의미합니다. SRP원리를 적용하면 무엇보다도 책임 영역이 확실해지기 때문에 한 책임의 변경에서 다른 책임의 변경으로의 연쇄작용에서 자유로울 수 있습니다. 뿐만 아니라 책임을 적절히 분배함으로써 코드의 가독성 향상, 유지보수 용이라는 이점까지 누릴 수 있으며 객체지향 원리의 대전제 격인 OCP원리뿐 아니라 다른 원리들을 적용하는 기초가 됩니다. 이 원리는 다른 원리들에 비해서 개념이 비교적 단순하지만, 이 원리를 적용해서 직접 클래스를 설계하기가 그리 쉽지만은 않습니다. 왜냐하면, 실무의 프로세스는 매우 복잡 다양하고 변경 또한 빈번하기 때문에 경험이 많지 않거나 도메인에 대한 업무 이해가 부족하면 나도 모르게 SRP원리에서 멀어져 버리게 됩니다. 따라서 평소에 많은 연습(‘책임’이란 단어를 상기하는)과 경험이 필요한 원칙입니다.

ii. 적용방법

리팩토링(Refactoring: Improving the Design of Existing Code -Martin Fowler)에서 소개하는 대부분의 위험상황에 대한 해결방법은 직/간접적으로 SRP원리와 관련이 있으며, 이는 항상 코드를 최상으로 유지한다는 리팩토링의 근본정신도  항상 객체들의 책임을 최상의 상태로 분배한다는 것에서 비롯되기 때문입니다.

여러 원인에 의한 변경 (Divergent change): Extract Class를 통해 혼재된 각 책임을 각각의 개별 클래스로 분할하여 클래스 당 하나의 책임만을 맡도록 하는 것입니다. 여기서 관건은 책임만 분리하는 것이 아니라 분리된 두 클래스간의 관계의 복잡도를 줄이도록 설계하는 것입니다. 만약 Extract Class된 각각의 클래스들이 유사하고 비슷한 책임을 중복해서 갖고 있다면 Extract Superclass를 사용할 수 있습니다. 이것은 Extract된 각각의 클래스들의 공유되는 요소를 부모 클래스로 정의하여 부모 클래스에 위임하는 기법입니다. 따라서 각각의 Extract Class들의 유사한 책임들은 부모에게 명백히 위임하고 다른 책임들은 각자에게 정의할 수 있습니다.

산탄총 수술(Shotgun surgery): Move Field와 Move Method를 통해 책임을 기존의 어떤 클래스로 모으거나, 이럴만한 클래스가 없다면 새로운 클래스를 만들어 해결할 수 있습니다. 즉 산발적으로 여러 곳에 분포된 책임들을 한 곳에 모으면서 설계를 깨끗하게 합니다. 즉 응집성을 높이는 작업입니다.

iii. 적용사례

[그림1.]의 간단한 클래스를 보겠습니다. 변화가 예상되는 부분이 보이시나요? 천천히 살펴보도록 하겠습니다.

https://www.nextree.co.kr/content/images/2021/01/srp1_before_640_416.png

[그림1.]SRP적용 전

위 [그림1.]에서 보는 바와 같이 serialNumber는 변화요소라 할 수 없고 단지 고유정보라고 할 수 있습니다. 동종의 다른 클래스와 구분되는 정보라고 할 수 있겠네요. 그리고 price와 Maker, Type, model, backWood, topWood, stringNum 등은 모두 특성 정보군으로 변경이 발생 할 수 있는 부분이라 할 수 있고, 이 부분은 변화 요소로 예상이 됩니다. 따라서 특정 정보군에 변화가 발생하면 항상 해당 클래스를 수정해야 하는 부담이 발생하게 됨으로 이 부분이 SRP적용의 대상이 됩니다.

https://www.nextree.co.kr/content/images/2021/01/srp2_after_src-e1400596694467.png

[그림2.]SRP적용후

위[그림2. ]의 다이어그램을 보면 변화가 예상되는 특성 정보군을 분리한 것을  확인 하실 수 있습니다. 따라서 특성 정보에 변경이 일어나면 GuitarSpec 클래스만 변경하면 됩니다. 훨씬 보기에도 좋아졌고 무엇보다 변화에의해 변경되는 부분을 한곳에서 관리할 수 있게 되었습니다.