
객체 지향 프로그래밍 및 설계의 다섯 가지 핵심 원칙을 SOLID라고 부르고 있다.
- 단일 책임의 원칙 (Single Responsibility Principle, SRP)
- 개방-폐쇄 원칙 (Open-Closed Principle, OCP)
- 리스코프 치환 원칙 (Liskov substitution principle, LSP)
- 인터페이스 분리 원칙 (Interface segregation principle, ISP)
- 의존성 역전 원칙 (Dependency Inversion Principle, DIP)
단일 책임의 원칙이란, 하나의 객체는 단 하나의 책임을 가져야 한다.
즉, 클래스나 모듈을 변경할 이유가 단 하나 뿐이어야 한다는 원칙이다.
만약 이를 지키지 않으면, 한 책임의 변경에 의해 다른 책임과 관련된 코드에 영향을 미치며 결국 유지보수가 매우 비효율적이게 된다.
여기서 책임이란?
💡 소프트웨어 엔티티 또는 개체(클래스, 모듈, 함수 등)는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다는 원칙이다.
- 즉, 소프트웨어 개체의 행위는 확장될 수 있어야 하지만, 개체를 변경해서는 안된다.
- 조금 더 쉽게 설명하자면, 기존 코드에 영향을 주지않고 소프트웨어에 새로운 기능이나 구성 요소를 추가할 수 있어야 한다는 것.
어떤 모듈의 기능을 수정할 때, 해당 모듈을 이용하는 모든 모듈 또한 수정한다면 유지보수가 복잡해진다. 따라서 OCP(개방-폐쇄 원칙)을 적용해서 기존 코드를 변경하지 않아도 기능을 수정, 추가할 수 있게 해야한다.
OCP를 지키지 않으면 객체지향 프로그래밍의 장점인 유연성, 재사용성, 유지보수성 등을 활용하지 못하게 된다.
기존의 코드를 변경하지 않고 어떻게 기능을 수정, 추가할 수 있을까?
상속(다형성), 추상화(인터페이스)를 활용하면 된다. 자주 변경하는 부분을 추상화해서 기존 코드를 수정하지 않고 기능을 확장할 수 있도록 해서 유연성을 살릴 수 있다.
💡 어플리케이션에서 객체는 프로그램의 동작에 영향을 주지 않으면서, 하위 타입의 객체로 바꿀 수 있어야 한다는 원칙.
- 즉,
S가T의 하위 유형이라면, 프로그램의 기능에 변화를 주지 않고서도T타입의 객체를S객체로 대체할 수 있어야한다.
우리가 부모 클래스(Parents)와 자식 클래스(Child) 를 가지고 있다면, 이 두가지의 클래스의 객체를 서로를 바꾸더라도 해당 프로그램에서 잘못된 결과를 도출하지 않아야하는 원칙이다.
예를 들어서, 자동차 인터페이스가 있다고 하자. 자동차 인터페이스의 엑셀 기능은 자동차가 앞으로 가는 기능을 한다. 그런데 엑셀 기능을 실행했는데 자동차가 뒤로 간다고 생각해보자. 참으로 끔찍한 일이다. 이는 LSP를 위반하는 것이다.
💡 클라이언트는 자신이 사용하는 메소드에만 의존해야 한다는 원칙이다.
- 특정 클라이언트를 위한 인터페이스 여러 개의 범용 인터페이스 한 개보다 낫다.
- 인터페이스는 해당 인터페이스를 사용하는 클라이언트를 기준으로 잘게 분리되어야 한다.
여기서 인터페이스란?
각 클라이언트가 필요로 하는 인터페이스를 분리함해서 클라이언트가 사용하지 않는 인터페이스에 변경이 발생해도 다른 인터페이스는 영향을 받지 않도록 만드는 것이 ISP의 핵심이다.
💡 프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다는 원칙이다.
- 프로그래머가 의존 관계를 맺을 때, 변하기 쉬운 구체적인 것 보다는 변하기 어려운 추상적인 것에 의존해야 한다는 것이다.
즉, 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻이다.- 높은 계층의 모듈(도메인)이 저수준의 모듈(하부구조)에 직접 의존해서도 안된다.