작성된 클래스는 하나의 기능만 가지며 클래스가 제공하는 모든 서비스는 그 하나의 책임을 수행하는데 집중되어 있어야 한다.
하나의 클래스에 여러 기능(책임)을 넣느냐, 클래스를 분리하여 기능(책임)을 분산시키느냐는 프로그램의 유지보수와 밀접한 관련이 있다.
단일 책임 원칙 준수 유무에 따른 가장 큰 특징 기준 척도는, '기능 변경(수정)' 이 일어났을때의 파급 효과 이다.
한 객체에 책임이 많아질수록 클래스 내부에서 서로 다른 역할을 수행하는 코드끼리 강하게 결합될 가능성이 높아지게 되어 한 책임의 변경에서 다른 책임의 변경으로의 연쇄작용이 일어 나게 된다.
SRP를 적용한다면, 각 클래스의 책임 영역이 확실해지기 때문에 어떠한 역할에 대해 변경사항이 발생했을 때, 그 책임을 지니고 있는 클래스만 수정해주면 된다.
이것을 다르게 말하면, 모듈이 변경되는 이유가 한가지 여야 함을 뜻한다. 여러가지 책임을 가지고 있으면 각기 다른 사유에 의해서 모듈이 변경되는 이유가 여러가지가 되기 때문이다.
한 클래스는 한 가지 책임에 관한 변경사항이 생겼을 때만 코드를 수정하게 되는 구조가 좋은 구조이다.
클래스는 자신의 이름이 나타내는 일을 해야 한다. 올바른 클래스 이름은 해당 클래스의 책임을 나타낼 수 있는 가장 좋은 방법이다.
무조건 책임을 분리한다고 SRP가 적용되는 것은 아니다. 각 객체 간의 응집력이 있다면 병합을, 결합력이 있다면 분리를 적용해야 한다.
소프트웨어의 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확장에는 열려있고, 변경에는 닫혀있어야 한다.
이것은 변경을 위한 비용은 가능한 줄이고 확장을 위한 비용은 가능한 극대화 해야 한다는 의미로, 요구사항의 변경이나 추가사항이 발생 하더라도, 기존 구성요소는 수정이 일어나지 말아야 하며, 기존 구성요소를 쉽게 확장해서 재사용할 수 있어야 한다는 뜻이다.
OCP는 관리가능하고 재사용 가능한 코드를 만드는 기반이며, OCP를 가능케 하는 주요 메커니즘은 추상화와 다형성이라고 설명한다. OCP는 객체지향의 장점을 극대화하는 아주 중요한 원리이다.
확장되는 것과 변경되지 않는 모듈을 분리하는 과정에서 크기 조절에 실패하면 오히려 관계가 더 복잡해질 수 있다.
이런 갈등 상황을 잘 포착하여 자신의 기준에 따라 결정을 내리는 것이 필요하다.
인터페이스는 가능하면 변경되어서는 안된다. 따라서 인터페이스를 정의할 때 여러 경우의 수에 대한 고려와 예측이 필요하다.
인터페이스 설계에서 적당한 추상화 레벨을 선택해야 한다. '추상화란 다른 모든 종류의 객체로부터 식별될 수 있는 객체의 본질적인 특징' 이라고 정의할 수 있다.
즉, 이 '행위'에 대한 본질적인 정의를 통해 인터페이스를 식별해야 한다.
서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다. 즉, 서브 타입은 언제나 기반 타입과 호환될 수 있어야 한다.
달리 말하면 서브 타입은 기반 타입이 약속한 규약 (public 인터페이스, 물론 메서드가 던지는 예외까지 포함된다)을 지켜야 한다.
상속은 구현 상속(extends)이든 인터페이스 상속(implements)이든 궁극적으로는 다형성을 통한 확장성 획득을 목표로 한다. LSP 원리도 역시 서브 클래스가 확장에 대한 인터페이스를 준수해야 함을 의미한다.
다형성과 확장성을 극대화하려면 하위 클래스를 사용하는 것보다 상위의 클래스(인터페이스)를 사용하는 것이 더 좋다.
일반적으로 선언은 기반 클래스로, 생성은 구체 클래스로 대입하는 방법을 사용한다.
상속을 통한 재사용은 기반 클래스와 서브 클래스 사이에 IS-A 관계가 있을 경우로만 제한 되어야 한다. 그 외의 경우에는 합성을 이용한 재사용을 해야한다.
상속은 다형성과 따로 생각할 수 없다. 그리고 다형성으로 인한 확장 효과를 얻기 위해서는 서브 클래스가 기반 클래스와 클라이언트 간의 규약(인터페이스)을 어겨서는 안된다. 결국 이 구조는 다형성을 통한 확장 원리인 OCP를 제공하게 된다. 따라서 LSP는 OCP를 구성하는 구조가 된다.
한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다.
즉, 어떤 클래스가 다른 클래스에 종속될 때에는 가능한 최소한의 인터페이스만을 사용해야 한다.
ISP를 '하나의 일반적인 인터페이스보다는, 여러 개의 구체적인 인터페이스가 낫다'라고 정의할 수도 있다. 만약 어떤 클래스를 이용하는 클라이언트가 여러 개고 이들이 해당 클래스의 특정 부분집합만을 이용한다면, 이들을 따로 인터페이스로 빼내어 클라이언트가 기대하는 메시지만을 전달할 수 있도록 한다.
SRP가 클래스의 단일책임을 강조한다면 ISP는 인터페이스의 단일책임을 강조한다.
하지만 ISP는 어떤 클래스 혹은 인터페이스가 여러 책임 혹은 역할을 갖는 것을 인정한다. 이러한 경우 ISP가 사용되는데 SRP가 클래스 분리를 통해 변화에의 적응성을 획득하는 반면, ISP에서는 인터페이스 분리를 통해 같은 목표에 도달한다.
구조적 디자인에서 발생하던 하위 레벨 모듈의 변경이 상위 레벨 모듈의 변경을 요구하는 위계관계를 끊는 의미의 역전 원칙이다.
실제 사용 관계는 바뀌지 않으며, 추상을 매개로 메시지를 주고 받음으로써 관계를 최대한 느슨하게 만드는 원칙이다.
DIP의 키워드는 'IOC', '훅 메소드'(슈퍼클래스에서 디폴트 기능을 정의해두거나 비워뒀다가 서브클래스에서 선택적으로 오버라이드할 수 있도록 만들어둔 메소드를 훅 메소드라고 한다. 서브클래스에서는 추상 메소드를 구현하거나, 훅 메소드를 오버라이드하는 방법을 이용해 기능의 일부를 확장한다.), '확장성'이다.
이 세 가지 요소가 조합되어 복잡한 컴포넌트들의 관계를 단순화하고 컴포넌트 간의 커뮤니케이션을 효율적이게 한다.
출처