CS/소프트웨어공학

SOLID

@~@ 2024. 7. 23. 23:42

[SE.A02] 

SOLID

1. SOLID 

로버트 마틴이 객체지향프로그래밍 및 설계의 다섯 가지 기본 원칙을 소개한 것이다. 원칙을 지킴으로써 코드 수정 및 구조 변경을 유연하게 할 수 있다. SOLID 는 함수와 데이터 구조를 클래스로 배치하는 방법과 이들 클래스를 서로 결합하는 방법을 설명한다. SOLID 의 5 대 원칙을 나열하면 다음과 같다. 

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

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

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

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

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

 

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

(1) 개념 

A class should have one, and only one, reason to change. 

모든 클래스(객체)는 각각 하나의 기능만 가져야 한다. 코드에 SRP 를 적용한다면, 각 클래스는 서로 다른 기능을 하나씩 담당하게 됨으로써 책임 영역이 확실해진다. 그래서 어떠한 역할에 대해 변경사항이 발생했을 때, 변경 영향을 받는 기능만 모아둔 클래스라면 그 책임을 지니고 있는 클래스만 수정해주면 된다. 즉, 모듈이 변경되는 이유가 한 개여야 함을 의미한다. 한 클래스는 한 가지 책임에 관한 변경사항이 생겼을 때만 코드를 수정하는 구조가 좋은 구조이다. 

 

(2) 이점 

다른 클래스들이 서로 영향을 미치는 연쇄 작용을 줄일 수 있다. 모듈 간 결합도를 낮추는 것이다. 또한 책임(기능)을 적절하게 분배하여 코드의 가독성을 향상시키고 유지보수에 용이해진다. SRP 원칙이 적용된다면 이는 이후 다른 원칙을 적용하는 기초가 된다. 

 

(3) 나쁜 디자인 – SRP 적용 전

이것은 직원 정보를 담당하는 Employee 클래스이다. 위 클래스에 초과 근무 시간을 계산하는 메소드인 calculateExtraHour()가 있다. 그런데 만약 초과 근무 시간을 계산하는 방법이 변경되어서 calculateExtraHour() 메소드의 코드가 수정된다면 이는 calculateExtrahou() 메소드를 멤버로 갖고 있는 calculatePay(), reportHours() 메소드에도 영향을 미칠 것이다. 이는 Employee 클래스가 한 개의 책임이 아닌 여러 개의 책임을 한 번에 지고 있기 때문에 일어난 상황이며, 이것이 SRP 에 위배되는 상황이다. 

 

(4) 좋은 디자인 – SRP 적용 후

 

위와 같이 각 책임(기능)에 맞게 클래스를 분리하여 구성하면 SRP 가 적용된 것이다. 회계팀에서 초과 근무 시간을 계산하는 방법이 변경되어도 EmployeeFacade 클래스는 건들지 않고 PayCalculator 라는 분리된 클래스의 메소드를 수정하면 되기 때문에 코드 간 결합도가 낮아졌음을 알 수 있다. 이 때 발생한 수정 사항은 다른 클래스와 메소드에 영향을 주지 않는다. 

 

3. OCP 

(1) 개념 

You should be able to extend a classes behavior, without modifying it. 

소프트웨어의 모든 개체는 확장에는 열려있어야 하고, 변경에는 닫혀 있어야 한다. 기존의 코드는 수정이 일어나지 말아야 하고, 쉽게 기능 확장이 가능하며 재사용할 수 있어야 한다는 의미이다. 여기서 확장은 새로운 기능 추가를 의미한다. 객체 간의 의존성을 최소화하여 코드 변경에 따른 영향력을 낮출 수 있다. 

 

(2) 장점 

다형성과 확장을 가능하게 하며 객체지향의 장점을 극대화한다. 확장엔 열려있는 유연한 구조를 만든다. 기존 코드를 크게 수정하지 않고 새로운 기능을 유연하게 추가할 수 있다. 

 

(3) 나쁜 디자인

만약 위 코드에서 ‘개’와 ‘고양이’ 외에 ‘사자’와 ‘코끼리’가 추가된다면 HelloAnimal 클래스에는 else if 문을 두 개 더 추가하여 사자와 코끼리의 울음소리를 출력하도록 코드를 수정해야 한다. 이는 새로운 기능(사자와 코끼리의 울음소리)을 추가하면서 기존의 코드가 수정되는 상황으로 OCP 에 위배되는 상황이다. 이렇게 되면 동물을 추가할 때마다 코드를 하나하나 수정해야 한다는 번거로움이 있다.

 

(4) 좋은 디자인 

위와 같이 변경될 요소(확장할 기능)인 speak 구현 코드를 분리하여 작성하고 수정하지 않을 부분인 speak 틀(프레임, 형식)을 인터페이스로 두어 새로운 기능을 추가할 때 기존의 코드가 수정되지 않도록 한다. 이는 구현체에 의존하기 보다 추상화에 의존하도록 한 방법으로 마지막에 소개할 SOLID 5 대 원칙 중 마지막인 DIP 와 관련된 부분이다.

 

4. LSP 

(1) 개념 

Derived classes must be substitutable for their base classes. 

서브 타입(하위 클래스)은 언제나 자신의 기반 타입(상위 클래스)으로 교체할 수 있어야 한다 부모 객체를 호출하는 동작에서 자식 객체가 부모 객체를 완전히 대체할 수 있어야 한다는 원칙이다. 올바른 상속을 위해 자식 객체의 확장이 부모 객체의 방향을 온전히 따르도록 권고한다. 이 원칙은 크게 신경 쓰지 않아도 인터페이스 구현, 상속 관계를 형성하면서 자연스럽게 따르고 있는 다형성과 관련된 규칙이다. 

 

(2) 장점 

다형성을 지원하고 유연한 코드 작성이 가능하다. 

 

(3) 나쁜 디자인

정사각형은 직사각형이므로 정사각형 클래스가 직사각형 클래스를 상속한 코드이다. 하지만 Rectangle square = new Square() 형태로 객체를 만들어 너비를 출력해보면 50 이 아닌 25 가 출력된다. 이는 두 클래스가 올바른 상속관계가 아님을 의미한다. 즉, 자식 객체가 부모 객체의 역할을 완전히 대체하지 못한다는 의미이다. 

 

(4) 좋은 디자인 

위 코드처럼 직사각형과 정사각형 클래스를 더 큰 범주인 사각형 클래스와 상속관계가 되도록 구현하면 LSP 원칙을 준수한 코드라고 볼 수 있다. 

 

5. ISP 

(1) 개념 

Make fine grained interfaces that are client specific.

자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다는 원칙이다. 어떤 클래스가 다른 클래스에 종속될 때는 최소한의 인터페이스만 사용해야 한다. 즉, 모든 기능이 포함된 하나의 일반적인 인터페이스보다 세세하게 구현된 인터페이스 여러 개가 낫다는 의미이다. 

 

(2) 장점 

인터페이스를 세부적으로 쪼개서 클래스의 역할에 맞게 상속 시킬 수 있다. 클래스의 기능을 쉽게 파악할 수 있고 유연하게 객체의 기능을 확장하거나 수정할 수 있다. 

 

(3) 나쁜 디자인 

위 코드에서 일부 스마트폰은 무선충전, AR, 생체 인식 기능이 탑재 되어 있지 않았을 수 있다. 

그럴 경우엔 인터페이스에 사용하지 않는 메소드, 불필요한 메소드가 포함되어 있기 때문에 이를 더 세세하게 나누어 구현하는 것이 올바르다. 

 

(4) 좋은 디자인 

이렇게 인터페이스를 분리하면 필요한 클래스에서만 골라서 사용할 수 있고 ISP 원칙을 준수할 수 있다.

 

6. DIP 

(1) 개념 

Depend on abstractions, not on concretions. 

상위 모듈은 하위 모듈에 의존해서는 안 된다. 모듈을 모두 추상화된 것에 의존해야 한다. 즉, 나보다 변하기 쉬운 것에는 의존하지 말아야 한다는 의미이다. 

 

(2) 장점 

유연성과 확장성을 가질 수 있다. 모듈 간 결합도를 낮추어 코드의 유지보수가 용이해진다. 

 

(3) 나쁜 디자인 

만약 위 코드에서 카드 결제가 아닌 현금 결제 방법으로 변경된다면, cardPayment 클래스처럼 cashPayment 클래스를 만들어서 Order 클래스에 추가해야 할 것이다. 이는 Order 클래스가 Payment 클래스에 의존하는 것으로 나보다 변하기 쉬운 것에 의존하면 안 된다는 DIP 원칙에 위배된다. 

 

(4) 좋은 디자인

위와 같이 코드를 변경한다면 Order 클래스는 결제 방법이 바뀌어도 코드를 수정할 필요가 없어진다. 

 

 

 

레퍼런스 

The Principle Of OOD 원문 http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod 

코드 레퍼런스 

https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EC%95%84%EC%A3%BC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-SRP-%EB%8B%A8%EC%9D%BC-%EC%B1%85%EC%9E%84-%EC%9B%90%EC%B9%99

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