SOLID는 객체 지향 프로그맹 및 설계의 5가지 기본 원칙으로
"로버트 C.마틴"이 2000년대 초반 명명하고 마이클 페더스가 정리한 원칙이다.
SOLID는 유지보수 및 확장성 향상과 개발 생산성 증대에 초점이 맞춰져 있는 원칙이다.
추상화와 다형성을 통한 유연한 설계를 핵심 가치로서 활용하고 있고, OOP의 4가지 특징인 "추상화, 상속, 캡슐화, 다형성"등을 개념을 재정립하여 만든 원칙이다.
SRP는 이름 그대로 단 하나의 책임만 가져야한다는 원칙이다.
책임이란 하나의 기능이나 역할을 의미하며 클래스가 특정한 역할에만 집중하도록 설계해야 한다는 의미이다.
원칙을 잘 따르면 클래스가 단순하고 유지보수가 용이해진다.
맥가이버 칼을 예시로 들어보면 "맥가이버 칼"은 여러 도구를 하나로 합쳐놓은 도구이다. 처음에는 편리해 보이지만 도구의 종류가 많이 질수록 크기와 복잡성이 증가하고 개별 기능의 성능이 떨어지는 경우가 발생한다.
이는 맥가이버 칼이 여러 책임을 동시에 수행하다보니 각 기능이 최적화 되지 못한 상황에 해당한다.
만약 각 도구가 본연의 역할만 수행하도록 분리된다면 각각의 기능을 보다 효과적으로 수행할 수 있을 것이다.
클래스도 마찬가지다. 하나의 클래스가 너무 많은 책임을 갖게되면 클래스의 역할이 불명확해지고 관리가 어려워지며 하나의 책임을 변경할 때마다 다른 책임까지 영향을 받을 가능성이 높아진다.
결국 단일 책임 원칙을 따르지 않는 클래스는 클래스 자체로서의 존재 가치를 잃게 된다.
SRP의 원칙을 준수하면 한개의 책임 변경으로부터 발생할 수 있는 사이드 이펙트에서 자유로워질 수 있고, 책임을 적절히 분배함으로써 코드의 가독성 향상, 유지보수 용이의 이점을 누릴 수 있게 된다.
OCP원칙은 기존 코드를 변경하지 않으면서 기능을 추가할 수 있도록 설계되어야한다는 원칙을 말한다.
확장에 대해서는 개방적(OPEN)이고, 수정에 대해서는 폐쇄적(CLOSED)이어야 한다는 정의이다.
OPC원칙은 추상화 사용을 통한 구축 권장을 의미한다.
또한 다형성과 확장을 가능케하는 객체지향의 장점을 극대화하는 설계원칙이다.
LSP 원칙은 "자식타입은 언제나 부모타입으로 교체할 수 있어야한다"라는 원칙이다.
LSP는 "다형성의 원리"를 얘기하는 것이다.
public void myData(){
//Collection타입 변수 선언
Collection data = new LinkedList();
data = new HashMap(); //중간에 타입을 변경해도 호환
modify(data); //메소드 실행
}
public void modify(Collection data){
//인터페이스 구현 구조가 잘 잡혀있기 때문에 add동작이 보장됨.
list.add(1);
...
}
LSP는 한마디로 다형성을 지원하기 위한 원칙.
ISP란 인터페이스를 잘게 분리함으로써, 클라이언트의 목적과 용도에 적합한 인터페이스만을 제공하는 것이다.
ISP는 클라이언트의 기준으로 목적과 용도에 맞게 분리하여 적합한 인터페이스만 제공하는 것을 원칙으로한다.
이와 비슷한 원칙이 하나 있는데 바로 SRP원칙이다. 복기하자면 SRP원칙은 "단일 책임 원칙"으로 클래스의 단일 책임을 강조한다면, ISP는 인터페이스의 단일 책임을 강조한다.
인터페이스는 다중 상속이 가능하기 때문에 분리할수 있으면 분리하여 원하는 기능을 조립하듯 사용하면 ISP의 원칙을 준수 할 수 있다.
DIP원칙은 구체적인 구현체에 의존하지 말고, 추상화된 상위 요소(인터페이스, 추상클래스)에 의존하라라는 원칙이다.
즉, "구현체가 아닌 추상화에 의존하도록 설계"하라. 라는 의미이다.
이를 통해 코드를 유연하고 확장성이 높도록 설계 가능하다.
구체적인 구현체에 의존하게 되면 해당 구현체가 변경될 때마다 코드를 수정해야하며 이는 시스템의 결합도를 높이고 유지보수를 어렵게한다.
하지만 DIP원칙을 준수하여 추상화에 의존하면 구현체가 변경되더라도 기존 코드를 수정하지 않아도되 결합도를 낮추고 시스템 안정성을 향상 시킬 수 있게 된다.
DIP원칙은 흔히 사용하는 Java Collection선언 방식에서 확인 가능하다.
List<String> list = new ArrayList<>();
Map<Long, String> map = new HashMap<>();
추상클래스인 List
에 의존하고 구현체인 ArrayList
를 생성자로 주입한다.
만약 ArrayList
를 LinkedList
로 변경해도 선언부만 수정하면 다른 코드는 수정하지 않아도 된다.