SOLID Principle
Why design patterns and SOLID principle?
- 코드의 작성, 유지보수, 변경, 확장을 쉽게 할 수 있도록 코드 작성하기 전부터 코드의 구조를 잘 정해두면 도움이 된다.
- 코드로 구현하고 싶은 기능의 유형에 따라 도움이 되는 구조에는 반복되는 패턴이 존재하는데, 이를 디자인 패턴이라고 부른다.
- 디자인 패턴은 본질적으로 이해하기 좋은, 그래서 수정-확장하기 좋은 코드를 짜는 것을 목적으로 하기에, 개별적인 디자인 패턴을 배우기 전에 디자인패턴의 일반 원리인 SOLID principle 을 알아보자.
1. Single Responsibility
모듈 클래스 함수는 하나의 프로그램 기능 중 하나의 파트에 대해서만 responsibility를 가져야 한다.
- 예를 들어서,
고양이
클래스 안에먹기
함수,걷기
함수가 포함되어있는 것은 고양이의 특성을 구현한다는 점에서 자연스럽지만상태출력
또는상태기록
함수가고양이
클래스 안에 존재하는 것은 single responsibility에 어긋난다고 볼 수 있다. - 이 경우,
고양이
클래스 안에먹기
함수,걷기
함수만 넣어두고,represent
함수 하나만 추가해서 그 리턴값을 클래스 외부에서출럭
혹은기록
할 수 있도록 하면,고양이
클래스는고양이
의 내재적 특성만 구현하는 single responsibility를 만족한다.
2. Open/Closed Principle
코드는 확장에는 열려있고, 수정에는 닫혀있도록 짜야한다.
- open/closed principle을 위반한 예시: 동물의 종류를 입력 받아, 그 종류에 따라 출력을 달리하는 함수를 생각해보자. (이미지의 hey 함수) 이 경우 동물의 종류를 확장할 때마다, 함수를 수정해줘야 한다. 즉, 기능을 확장할 때마다 매번 기존 코드를 수정해줘야하는 불편함이 발생한다.
- 해결 방법? 클래스 상속, 인터페이스 클래스, 추상 클래스 등을 사용해서 다른 동물을 추가할 때, 새로운 클래스를 만들어 코드를 확장할 수 있게 만들자. 이렇게 하면, 기존 코드에 손을 댈 필요가 없다.
- Animal 부모 클래스을 만들고,
- Cat, Dog 클래스가 이를 상속하도록 구조를 짜놓았다고 해보자.
- 이 상태에서 Sheep이나 Cow를 추가확장하고 싶으면, Sheep 클래스와 Cow 클래스를 덧붙이기만 하면 된다.
- 이러면, 기존의 짜놓은 코드를 변경할 필요가 없게 된다!!
3. Liskov Substitution Principle
$S$ 가 $T$ 의 subtype 일 때, type $T$ 의 object 는 type $S$ 의 object로 치환 할 수 있다.
- 상위 클래스의 객체를 이용해 코드를 짠 경우에, 해당 객체를 하위 클래스의 객체로 바꿔서 선언하더라도 (치환하더라도), 코드가 잘 돌아가야 한다.
- 이를 위해선, 부모클래스의 속성/메서드가 자식클래스의 속성/메서드에 포함되어 있어야 한다.
- 잘못 된 예로는,
- 자식클래스가 속성이나 메서드 오버라이딩 할 때, 타입/리턴값 갯수를 다르게 바꾸는 경우,
- 메서드 오버라이딩할 때, 부모클래스의 의도와 다른 기능을 구현한 경우가 있다.
4. Interface Segregation
Client를 사용하지도 않을 메소드에 의존하게 만들어서는 안된다.
- 다시 말해, 큰 인터페이스를 작은 인터페이스로 나누는 것이 좋다.
- 인터페이스(혹은 추상 클래스)는 내부 메서드를 선언만 하고, 이를 상속받은 클래스가 실제 메서드를 구현하는 클래스를 말한다.
- 따라서.. 인터페이스는 클래스를 만들기 위한 틀을 제공한다고 볼 수 있다. 그럼 한 그릇에 많은 걸 담고 싶은 욕망 때문에, 하나의 인터페이스가 여러가지 일을 하고 싶게 만들 수 있는데 (예를 들어 잠수함의 기능과 자동차의 기능을 선언한 인터페이스)
- 이 경우, 인터페이스를 이용해 만든 클래스 (자동차 기능을 구현한 소나타 클래스) 에는 필요도 없는 잠수함의 기능이 들어가야 한다. 따라서,
자동차 인터페이스
와잠수함 인터페이스
를 따로 만들어 사용하는 것이 좋다. - 그럼
잠수 기능이 있는 자동차
만들고 싶으면 어떡하지? 하는 호기심이 생길 수 있는데, 이 경우에는자동차 인터페이스
와잠수함 인터페이스
를 둘다 상속 받는 식으로 해결할 수 있다.
5. Dependency Inversion Principle
- 모듈이 계층화 되어있을 때, 높은 레벨의 모듈은 낮은 레벨의 모듈을 직접 포함해 의존성이 생기면 안된다.
- 어떻게? 높은 레벨의 모듈과 낮은 레벨의 모듈 모두 interface나 추상 클래스같은 abstractions에 의존하도록 만들면 된다
- 이 때 사용되는 인터페이스는 구현 클래스에 의존하면 안되고, 구현클래스가 인터페이스에 의존하도록 만들어야 한다.
-
동물원
안에고양이
와강아지
가 살고 있으면, - 이를 구현할 때,
동물원
클래스가고양이
클래스와강아지
클래스를 포함하도록 직관적으로 코드를 짤 수 있다. - 다시 말해,
동물원
클래스가 여러 동물들에 대해 의존성이 생기도록 코드를 짤 수 있다. 이는 상위 레벨의 개념이 하위 레벨 개념을 포함하는 측면에서 꽤 자연스러운 구조라고 할 수 있다. - 그런데 여기서 문제가 발생한다.
동물원
에 다른 동물들양
,소
같은 애들이 더 입주하면, 매번동물원
클래스를 수정해줘야 하는 문제가 생긴다. - 여기에서 Dependency Inversion 을 해주면 문제가 해결된다.
- 구체적으로는,
동물
추상클래스를 만들고,동물원
과 여러 동물들이 모두동물
추상클래스에 의존하게 만들면, - 새로운 동물을 추가할 때, 기존의 코드를 전혀 수정할 필요가 없게 된다 !!
Reference
Notes Mentioning This Note
There are no notes linking to this note.