프로그래머가 시간이 지나도 유지 보수 및 확장이 쉬운 시스템을 만들고자 할 때 이 원칙들을 함께 적용할 수 있다. SOLID 원칙들은 소프트웨어 작업에서 프로그래머가 소스 코드가 읽기 쉽고 확장하기 쉽게 될 때까지 소프트웨어 소스 코드를 리팩터링하여 코드 냄새를 제거하기 위해 적용할 수 있는 지침이다.
하나의 엔티티(모듈, 클래스, 함수)는 하나의 책임만 가져야한다!
add
함수는 더하기 책임만 있다(O)print
함수는 출력 책임만 있다(O)add_print
함수는 더하기 + 출력 책임이 있다(X)
고양이에게 있어 먹기, 달리기, 점프하기는 자연스러운 기능이지만 출력이라는 기능을 부자연스럽다. 즉, 고양이 클래스에는 오로지 고양이를 위한 기능만 존재해야 하는데 해당 클래스에 출력 기능을 넣는 것을 올바르지 않다.
따라서 고양이 클래스로부터 출력 함수를 외부로 분리함으로써 Cat
클래스는 오직 고양이를 위한 클래스로 만든다.
코드는 확장에 있어 개방되어 있어야하며, 수정에 있어서는 닫혀있어야한다
Animal
클래스를 정의하고 hey
라는 함수에는 Animal 의 Dog, Cat 에 대해서 타입 별로 분기처리하여 특정 문자열을 출력하도록 되어있다.
만약 Cow, Sheep 이라는 타입의 Animal 이 등장한다면 hey
라는 함수에 또 다른 분기처리를 해주어야만 한다 (OCP 원칙 위반)
이를 해결하기 위해 추상화클래스를 만든 후 이를 상속받아 동물들의 클래스를 정의해준다. 이때 hey
함수에서 담당하던 출력을 각각 동물에서 정의하고 hey
함수에서는 동물의 speak
를 호출하도록 바꿔 확장할 때 hey
함수를 수정하지 않고(Closed) 여러 동물을 만들 때도 확장하기 쉽게 만든다(Open)
프로그램 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
Cat
이라는 상위 클래스를 상속받는 하위 클래스 중 BlackCat
, WhiteCat
은 자연스럽지만 상식적으로 Fish
는 부적절하다. 간단한 예시로 고양이는 '야옹'이라는 소리를 낼 수 있어야한다. 그러나 물고기는 별다른 소리를 내지 않는다. 이를 코드로 표현하면 다음과 같다.
Cat
이라는 상위 클래스를 상속받는 하위 클래스 중 BlackCat
, WhiteCat
에서는 각각 소리를 내는 speak
함수가 정의되어 있지만 Fish
하위 클래스에서는 소리를 내는 speak
함수를 구현할 수 없으므로 물고기 인스턴스는 고양이 인스턴스를 대체할 수 없다.
리스코프 치환 원칙은 객체지향의 다형성과 유사하지만 다음과 같은 부분에서 개념이 나눠진다.
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다!!
큰 인터페이스를 작은 단위로 나누는 것이 좋다!
수륙양용은 지상과 물에서 운전이 가능한 차이다. 이에 대해 인터페이스를 정의하면 다음과 같다
- 물에서 직진
- 물에서 좌회전
- 물에서 우회전
- 땅에서 직진
- 땅에서 좌회전
- 땅에서 우회전
하지만 이렇게 인터페이스를 구성할 경우 이후 해당 인터페이스를 이용하여 차나, 배를 만들 수 없다. 확장성을 고려하여 다음과 같이 나눠보자!
- 물에서 직진
- 물에서 좌회전
- 물에서 우회전
- 땅에서 직진
- 땅에서 좌회전
- 땅에서 우회전
추상화에 의존하자! 구체화에 의존하지 말고!
high level 에서 low level 에 의존할 경우, low level 이 늘어남에 따라 high level 에서는 주입해야할 의존성들이 늘어난다. 이를 위해 의존성을 역전시켜 확장하더라도 의존성을 줄이는 방법을 소개한다.
동물원(high level) 에서 사자, 호랑이(low level) 에 의존하고 있다. 만약 동물이 늘어나면 다음과 같이 의존성이 늘어난다.
만약 Animal 이라는 추상화를 만들고 동물원과 동물들이 이를 의존하게 만들면 어떻게 될까?
이를 코드로 나타내면 다음과 같다
Zoo 클래스에서는 새로운 low level 이 생길 때마다 수정해야 될 필요가 사라졌다. 즉, 의존성이 줄어들어 확장에 용이해진 것이다