본문 바로가기
General

객체지향 설계 원칙 - SOLID

by whatamigonnabe 2022. 11. 14.

들어가며

벌써 우테코 프리코스 3주 차입니다. 코드스테이츠의 메인 프로젝트와 겹쳐서 바쁜 하루하루를 보내고 있지만, 바쁜 건 좋은 거라고 생각합니다ㅎㅎㅎ

아무튼 이번주의 주제는 클래스의 분리 단위 테스트입니다. 둘 다 하루아침에 되는 것이 아닐 것을 알지만, 제대로 개념들을 생각하며 아직 진행해본 적이 없어, 이번 기회에 클래스의 분리에 대해서 서칭을 해보았습니다. 찾다 보니, SOLID라 불리는 객체지향 설계 원칙이라는 개념을 알게 되어 , 우선 공부하며 정리해두고 적용하는 연습을 하려 합니다.

 

WHAT is SOLID?

SOLID 로버트 마틴 2000년대 초반에 명명한 객체 지향 프로그래밍  설계 다섯 가지 기본 원칙을 마이클 페더스가 두문자어 기억술 소개한 것이라고 합니다. 이 원칙의 목적은 가독성과 확장성을 높이고 유지보수를 쉽게 하는 것입니다. 프로그래밍에 대해서 공부할수록, 모든 원칙의 근간이 되는 목적인 것 같다는 생각이 듭니다.
그럼 다섯 가지 원칙 중 S부터 시작해보겠습니다.

 

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

이름에서 파악할 수 있다시피, 모든 클래스(객체)는 단 하나의 책임만 있어야 하며, 하나의 클래스를 수정하는 이유는 단 가지만 있어야 합니다.

예를 들어, 사용자로부터 어떤 값을 입력받아 저장하는 기능을 수행하는 클래스가 있다면, 이 클래스는 입력과 저장이라는 두 가지의 역할을 수행하고 있음으로, 분리해야 할 것입니다.

 

책임을 최대한 작게 분리하는 이유는, 연쇄적인 영향으로부터 자유로워지고 그럼으로써 클래스가 더욱 단단해지기 때문입니다. 만약 위에서 예로 든 입력과 저장을 하는 클래스의 경우, 입력 기능을 수정했을 때 저장에 영향을 줄 수도 있습니다. 또한 클래스를 분리할수록가독성이 올라갑니다. 하나의 기능을 직관적으로 이해할 수 있는 클래스 명을 붙이게 된다면, 코드의 흐름을 훨씬 쉽게 이해할 수 있을 것입니다. 또 여러 기능이 얽혀있지 않기 때문에, 테스트하기에 용이합니다. 또한 코드를 분리할수록재사용에 유리하고, 이 뜻은 산발적으로 반복되던 코드를 하나의 클래스에서 관리함으로 유지보수도 용이해집니다.

 

그래서 어떻게 적용하는가?

한 클래스 안에 여러 책임들이 산재되어 있다면, 하나의 클래스로 분할하여 하나의 책임만 맡도록 합니다. 그런데 만약 분할된 클래스들이 유사하다면, 공통되는 요소들을 Super Class를 만들어 책임을 위임해야 합니다.

 

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

소프트웨어 구성요소(클래스, 메서드 등)의 확장에는 열려있고, 변경에는 닫혀있어야 한다는 원칙입니다. OOP에서는 추상화와 다형성을 통해 이를 구현할 수 있고, 관리 가능하고 재사용이 가능한 코드를 만들 수 있습니다.

 

적용방법

1. 확장될 것이라고 예상되는 부분들을 정하고,

2. 해당 부분을 인터페이스로 구현한다.

3. 그리고 이 구현에 의존하지 말고, 인터페이스에 의존하도록 한다.

 

예를 들어, 악기 클래스를 만든다고 할 때, 어떤 악기이든지 '소리를 내는 기능'은 동일하고 이 소리는 여러 악기의 종류마다 다릅니다. 즉 확장이 예상되는 부분입니다.

그렇다면, 악기라는 인터페이스에 소리 내기라는 메서드를 명시하여 구현하고, 피아노 / 기타 등의 악기 클래스들이 이를 구현하게 합니다.

그리고 사람이라는 클래스가 악기를 연주할 수 한다면, 즉 악기에 의존한다면, 각 구현체들을 확장하더라도 의존하는 클래스는 수정할 필요가 없습니다.

 

LSP: the Liskov Substitution Principle (리스코프 치환 원칙)

하위 타입은 언제나 상위 타입으로 호환할 수 있어야 한다는 원칙입니다. 이 원칙은 OCP의 구조를 나타나는 것으로 이해할 수 있다고 합니다.

 

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

인터페이스의 단일 책임 원칙을 나타냅니다. 인터페이스에 의존하는 여러 클래스들이, 해당 인터페이스 기능의 일부만을 쓴다면, 더 구체적으로 분리해야 합니다.

 

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

추상화에 의존하고, 구현체가 의존하지 않는 원칙입니다. 위에서 설명했던 것과 같이, inferface에 의존하게 되면, 관리가 더욱 간단한고 유지보수가 편합니다.

 

결론

5가지 원칙이라고는 하지만, 제가 느끼기로는 크게 두 가지를 신경 쓰며 클래스를 설계해야겠다는 생각이 들었습니다

첫 번째는, 클래스든 인터페이스든 한 가지의 책임만 갖도록 분리해야 한다.

두 번째는, 확장될 것으로 예상되는 것은 인터페이스로 구현하고, 이것의 구현체가 아닌 인터페이스에 의존하도록 하는 것입니다.

 

아직 크게 와닿지는 않지만, 위의 두 가지를 신경 쓰며 개발해야겠습니다!

 

 

참조

https://doublem.org/SOLID_SRP_OCP/

https://www.nextree.co.kr/p6960/

https://ko.wikipedia.org/wiki/SOLID_(% EA% B0% 9D% EC% B2% B4_%EC% A7%80% ED%96% A5_%EC%84% A4% EA% B3%84)