SOLID ?Single Responsibility Principle(단일 책임 원칙)여러가지 책임을 하고 있는 경우한가지 책임만 하는 경우Open-Closed Principle(개방-폐쇄 원칙)예시Liskov Substitution Principle(리스코프 치환 원칙)Interface Segregation Principle(인터페이스 분리 원칙)Dpendency Inversion(의존성 역전 원칙)강하게 종속되어 있는 경우의존이 역전된 경우REF
SOLID ?
Single Responsibility Principle(단일 책임 원칙)
- 클래스는, 오직 하나의 대해서만 책임져야 한다.
- 만약 클래스가 여러가지 작업을 책임져야 한다면, 이는 버그 발생 가능성을 높이게 됩니다. 또한 많은 기능 중 한가지를 변경할 때 모르는 사이에 다른 기능에 영향을 줄 수 있기 때문이다.
- SRP의 목적은 행동을 분리하는 것이고, 이로 인해 어떤 기능을 수정하더라도, 연관없는 기능에는 영향이 가지 않게 될 것이다.
여러가지 책임을 하고 있는 경우
- 아래의 예시는 캐릭터가 검사일때와 궁수일때의 책임을 수행하고 있으며 두 가지의 책임을 가지고 있으며 응집도가 낮다고 할 수 있다.
- 다른 책임과 position이라는 필드를 공유하고 있는 것처럼 의존도가 높아 결합도가 높다고 할 수 있다.
한가지 책임만 하는 경우
- 아래의 클래스는 추상클래스를 통해 검사와 궁수의 책임을 분리하여 각 하나의 책임만 맡고 있기 때문에 응집도가 높으며 의존도가 존재하지 않기 때문에 결합도도 낮다고 할 수 있다.
Open-Closed Principle(개방-폐쇄 원칙)
- 클래스는 확장에는 개방적이어야 하고, 변경이는 폐쇄적이어야 한다.
- 클래스의 현재 코드를 변경하는것은 해당 클래스를 사용하고 있는 모든 시스템에 영향을 주게 된다.
- 만약 클래스에 더 많은 기능을 부여하고 싶다면, 가장 이상적인 접근방법은 기존 기능을 변경하는것이 아닌 새로운 함수를 추가하는 것이다.
- OCP의 목적은 클래스의 존재하는 기능의 변경 없이 해당 클래스의 기능을 확장시키는 것이다. 이로 인해 사용중인 클래스의 변경으로 인한 버그 발생을 피할 수 있다.
예시
- 아래는 단일책임 원칙에 사용했던 예시로 새로운 직업이 만약에 새로 생긴다 해도 다음과 같이 다른 클래스의 변경 없이 단순히 새로운 직업에 대한 클래스를 추가하기만 하면 된다.
Liskov Substitution Principle(리스코프 치환 원칙)
- 만약 S가 T의 서브타입이라면, T는 어떠한 경고도 내지 않으면서, S로 대체가 가능하다.
- 자식 클래스가 부모클래스의 기능을 똑같이 수행할 수 없을때, 이는 버그를 발생시키는 요인이 된다.
- 만약 어떤 클래스가 자신으로부터 다른 클래스를 생성했다면, 이제 그 클래스는 부모 클래스가 되고, 생성된 클래스는 자식 클래스가 된다. 자식 클래스는 부모 클래스가 할 수 있는 모든 것을 할 수 있어야 한다. 이를 상속이라고 표현한다.
- 자식 클래스는 부모 클래스처럼 똑같은 요청에 대해 똑같은 응답을 할 수 있어야 하고, 응답의 타입 또한 같아야 한다.
동일한 값을 넣었는데 다른 값을 반환하고 있으며 직사각형과 정사각형은 상속관계가 될 수 없는 구조이다.
사각형의 특징을 갖고 있지만 두 사각형 모두 사각형의 한 종류일 뿐이지 하나가 다른 하나를 완전히 포함하진 못한다.
Interface Segregation Principle(인터페이스 분리 원칙)
- 클라이언트는 사용하지 않는 메서드에 대해 의존적이지 않아야 한다.
- 클래스가 서로 관계없는 기능들을 가지고 있다면 낭비가 되고, 예상치 못한 버그를 발생시킬 수 있다.
- 클래스는 해당 역할에 대해 액션만 수행해야 하고, 이를 제외한 다른 액션은 완전히 삭제하거나 다른 곳(다른 클래스 등)으로 이동시켜야 한다.
- ISP의 목적은 액션 집합을 더 작은 액션 집합으로 쪼개서, 클래스가 필요한 액션들만 실행할 수 있도록 하는 것이다.
Dpendency Inversion(의존성 역전 원칙)
- 추상은 구체에 의존하지 않아야 하며, 구체는 추상에 의존해야 한다.
- 고수준의 모듈은 저수준의 모듈에 의존적이면 안되고, 둘다 추상에 의존적이어야 한다.
- 고수준 모듈(또는 클래스) : 도구와 함께 동작하는 클래스
- ex) 바이트 데이터를 읽어와 암호화 하고 결과 바이트 데이터를 쓴다.
- 저수준 모듈(또는 클래스) : 수행하기 위한 도구
- ex)
- 파일에서 바이트 데이터를 읽어온다.
- AES 알고리즘으로 암호화한다.
- 파일에 바이트 데이터를 쓴다.
- 추상 : 두 클래스를 연결하는 인터페이스
- 구체 : 도구가 동작하는 방법
- DIP는 액션을 수행할 때 클래스가 도구와 융합하면 안된다고 말한다. 보다 좋은 방법은 인터페이스와 융합하여 클래스와 도구를 연결하는 것이다.
- 두 클래스와 인터페이스는 어떻게 도구가 동작하는지 알 수 없어야 한다. 하지만, 도구는 인터페이스 사양을 충족해야 한다.
- DIP의 목적은 인터페이스를 통해 고수준 클래스이 저수준 클래스에 대해 의존성을 가지는 것을 줄이는 것이다.
강하게 종속되어 있는 경우
- 코드를 아래처럼 작성한다면 키보드와 마우스에 강하게 종속된다. 즉 맥미니는 특정 키크론 키보드와 로직텍 마우스밖에 사용할 수 없게 된다 만약 이 맥미니에 키크론키보드가 아니라 다른 키보드 브랜드를 사용한다고 하면 무조건 맥미니 클래스를 수정해야 한다.
- 즉 확장에 닫혀있고 수정에 열리게 된다.
의존이 역전된 경우
- 맥미니가 인터페이스에 의존하게 되면서 인터페이스를 확장한 모든것을 사용할 수 있게되며 확장에는 열리고 수정에는 닫히게 된다.