OOP design principles, SOLID
리팩토링을 하거나, 클래스등을 디자인할 때 가이드라인이 되어 줄 수 있는 몇 가지 원칙이 있다. Uncle Bob 이라고 알려져 있는 Robert C Martin 이 정리한 것인데, 다음과 같다.
- Single responsibility principle
- Open/Closed principle
- Liskov substitution principle
- Interface segragation principle
- Dependency inversion principle
앞 글자를 따서 SOLID 원칙이라고 한다. 각각에 대해 간단하게 살펴보자.
Single responsibility Principle(SRP : 단일책임의 원칙)
하나의 객체는 한가지 일에만 집중하도록 한다는 이야기다. Pitcher 라는 클래스가 있으면 공 던지는 일에 대한 일을 하도록 디자인 되어야지, 방망이를 휘둘러 공을 치는 일까지 담당하지 않도록 해야 한다.
Open/Closed Principle (OCP : 개방폐쇄의 원칙)
Open for extension, Closed for modification. 새로운 기능을 확장하기는 좋아야 하나, 기존의 것을 수정하는 것은 막아야 한다라는 뜻인데, Object 개념이 이미 자리 잡은 상태에서는 이 문장이 쉽게 수긍이 갈 수 있으나, 그 외의 경우라면 모순처럼 보일 수도 있다. 어쨌든 이 원칙이 이야기하고자 하는 바는 Abstraction 을 통해 객체의 인터페이스를 상속을 거쳐 확장 가능하게 함으로써 기존의 소스코드는 가능하면 변경을 막겠다는 것이다.
Liskov substitution Principle(LSP : 리스코브 치환의 원칙)
if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program
리스코프 치환 원칙. 자식 클래스는 부모 클래스의 역할도 할 수 있어야 한다는 것이다. 확장된 인터페이스를 사용하다가, 기반 인터페이스를 쓰더라도 문제 없이 작동한다는 것을 보증한다. 일종의 하위 버전 호환성, backward compatibility 와도 유사하다.
Interface Segragation Principle (ISP : 인터페이스 분리의 원칙)
인터페이스를 명확히 나누어서 정의해두라는 뜻이다. 하나의 통짜 인터페이스에 필요한 메소드들을 모두 선언해두지 말고, 각각의 용도에 따라 나눠서 인터페이스를 선언하도록 한다. 그렇게 나눈 뒤 각 인터페이스들이 공유하는 부분이 발생하면, 이것을 따로 빼어 재사용할 수 있게 수정한다.
Dependency Inversion Principle (DIP : 의존성역전의 원칙)
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.
B 항은 위의 LSP(Liskov Substitution Principle) 와도 일맥상통하는 부분이 있다. A. 항은 용어 그 자체로 선뜻 이해되지 않는데, 여기서 말하는 dependency 는 사용관계, UML 에서 말하는 aggregation 에 해당한다. 즉 인터페이스 위부의 어떤 코드가 (high-level module) 가 인터페이스 내부의 concrete class 를 직접 쓰지 말고 interface 를 통해야 한다는 것.
예를 들어 어떤 라이브러리에 Apple 이라는 클래스가 있다고 하자. 이 클래스는 IFruit 이라는 인터페이스를 상속받았다고 한다면, 이 라이브러리를 쓰는 코드는 다음과 같은 식으로 코딩한다.
IFruit firstObj = new Apple(); human.Eat(firstObj);
이렇게 함으로써 현재 코드 -> 라이브러리내의 Apple 클래스에 대한 dependency 를 IFruit 을 통하게 되었다. 인터페이스를 통하지 않았다면,
Apple firstObj = new Apple(); human.Eat(firstObj)
이렇게 코딩하게 되어 현재 코드가 라이브러리 내의 Apple 이라는 클래스에 dependency 가 생겼지만, 인터페이스를 통하게 함으로써 Apple 을 포함한 라이브러리가 IFruit 라는 인터페이스를 구현하도록 강제했다. 이 강제성으로 인해 dependency 가 low-level 에서 high-level 쪽으로 뒤집혔다고 보게 된다는 뜻.
출처 : https://realgsong.wordpress.com/2011/10/26/oop-design-principles-solid/comment-page-1/#comment-1194