너무나 유명하고 OOP의 본질을 꿰뚫는 중요한 원칙이지만 실상 코딩에서는 자주 잊게 되고, 잊지 않더라도 여러가지 현실에 부딪혀 위반을 묵인하게 되는 원칙.
자세한 내용은 검색 한 번으로 무수히 많은 잘 정리된 문서를 찾을 수 있으니, 여기에는 '이 원칙 적용엔 이것만 생각하면 된다' 정도만을 적어두기로 한다.
여기서 '주제영역' 이란, DB Access, File Handling, XML Converting 과 같은, 하나의 독립적인 주제를 가지는 영역을 말한다.
1. 단 하나의 책임 원칙(The Single Responsibility Principle, SRP)
-어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다.
위반 의심 포인트: 두 가지 서로 다른 주제영역의 변경사항으로 인해 하나의 클래스를 변경해야 할 때. (하나의 클래스가 둘 이상의 주제영역에 의해 영향을 받을 때)
원칙 적용 조치:
A. 주제영역을 다루는 클래스를 따로 만들어 주제영역을 분리하고, 변경해야 하는 클래스와 새로 만들어진 클래스를 연관짓는다.
B. 강결합을 약결합으로 변경한다. (로직을 담은 클래스가 인터페이스를 직접 구현하도록 하지 않고, 인터페이스와 클래스를 구현하는/상속받는 클래스를 새로 만든다.)
2. 개방-폐쇄 원칙(The Open-Closed Principle, OCP)
-소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에 대해서는 개방되어야 하지만, 변경에 대해서는 폐쇄되어야 한다.
클래스를 변경하지 않고도 그 클래스의 환경을 바꿀 수 있어야 한다.
위반 의심 포인트: 주제영역(환경)에 변경이 있을 때, 그 주제영역에 연관된 엔티티(클래스, 모듈, 함수 등)까지 변경해야 하는 경우(보통 주제영역과 엔티티 사이에 강결합이 맺어져 있을 때가 그렇다.)
원칙 적용 조치: 주제영역과 약결합을 맺는다. (주제영역의 인터페이스를 만들고 엔티티는 인터페이스를 통해 주제영역에 접근하도록 한다. 주제영역의 변경 영향도를 인터페이스의 구현체에 한정한다.)
3. 리스코프 교체 원칙(Liskov Substitution Principle, LCP)
-서브타입은 언제나 자신의 기반 타입(base type)으로 교체할 수 있어야 한다.
클라이언트가 인터페이스나 추상클래스를 사용할 때, 그의 컨크리트 클래스에 대해서는 알 필요가 없어야 한다.
위반 의심 포인트: 어떤 인터페이스를 사용하는데, 구현체에 따라 로직을 구분하기 위해 instanceof 나 다운캐스트(downcast) 를 필요로 할 때.
원칙 적용 조치: instanceof 나 downcast 가 필요한 구현체를 다른 인터페이스의 것으로 분리한다. 이 위반이 발생함은 애초에 추상화가 잘못 되었음을 의미한다.
4. 의존 관계 역전 원칙(Dependency Inversion Principle, DIP)
-고차원 모듈은 저차원 모듈에 의존하면 안 된다. 이 두 모듈 모두 다른 추상화된 것에 의존해야 한다.
-추상화된 것은 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상화된 것에 의존해야 한다.
위반 의심 포인트: 강결합이 발견될 때.
원칙 적용 조치: 추상화를 통해 인터페이스나 추상클래스를 만들어 강결합을 약결합으로 변경한다. 컨크리트 클래스에 의존하지 않는다.
5. 인터페이스 격리 원칙(Interface Segregation Principle, ISP)
-클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다.
위반 의심 포인트: 객체A 에서 객체B 를 사용하는 경우, 객체B 가 객체A 에게 필요없는 메서드를 제공할 때
원칙 적용 조치: 객체A 에게 필요한 메서드만을 가진 인터페이스를 새로 만들어 객체B 의 메서드를 여기로 이동시킨다. 객체B 는 이 인터페이스를 구현하도록 하며, 당연히 객체A 는 이 새로운 인터페이스를 통해 객체B 를 사용한다.
결국은 아래 두 가지로 거의 모든 것이 정리된다.
1. 주제영역 분리
2. 추상화를 통한 강결합의 약결합화