승쨩개발공부
[Notion] SOLID원칙(앞으로 코드 치기전에 한번씩 보자) 본문
포폴 제작하는데 문득 코드를 작성하면서
아.. 뭔가 객체지향이 문득 아닌거같다는 생각이들어서 다시한번 개념을 정리를 하려고 한다.
1. 개념
S : 단일책임 원칙 (Single Responsiblity Principle)
O : 개방 폐쇄 원칙 (Open/Closed Principle)
L : 리스코프 치환 원칙 (Liskov's Substitution Principle)
I : 인터페이스 분리 원칙 (Interface Segregation Principle)
D : 의존성 역전 원칙 (Dependency Inversion Principle)
1. 단일 책임 원칙
하나의 클래스는 하나의 책임만 갖는다 / 클래스의 코드가 수정되어야 할 이유는 하나여야 한다
간혹 하나의 클래스에 여러가지 기능을 한번에 구현하는 경우가 종종 있는데 이건 단일 책임의 원칙을 위배하는것이다.
ex:Player 클래스에 입력,이동,사운드 등을 한번에 구현함 이것은 단일 책임 원칙 위반이다
Audio,Input,Movement클래스를 구현해 이것들을 각각 별도의 컴포넌트로 오브젝트에 부착해야한다
효과 : 가독성이 좋아짐, 확장성이 좋아짐, 재사용성이 좋아짐
2. 개방 폐쇄 원칙
클래스가 확장에는 개방되어있고 수정에는 닫혀있어야한다
즉 원본 코드를 수정하지 않고 새로운 동작을 추가할 수 있어야한다.
Calculator 라는 클래스가있는데 각각 사각형과 원의 넓이를 구하는 함수를 구현하고있다.
근데 오각형이나 육각형 같은 더 많은 도형이 추가되어야한다면 그떄마다 이 클래스가 계속해서 수정되어야한다.
수정이 잦아지면 여러 휴먼에러가 발생할 가능성이 높아진다.
그래서 수정하지 않고도 계속 기능을 추가할 수 있도록 코드를 설계해야한다.
해결법
Shape이라는 부모 추상 클래스를 만든다
Shape을 상속받아 별도의 사각형,원 클래스를 만들어서 넓이를 구해준다.
도형별로 필요한 변수들을 만들어주고 상속받은 추상 클래스를 오버라이드해서
각 도형별로 넓이를 구하는 공식을 작성해준다.
이제 Calcuator에서 Shape을 매개변수로 받고 return으로 던져주면된다.
이렇게 된다면 도형이 더 추가되더라도 기존 코드가 전혀 수정되지 않는다.
도형이 더 추가된다면 새로운 클래스를 만들어서 Shape을 상속시켜주기만하면 된다.
즉 기존 코드는 수정은 할 게 없어지고 새로운 코드만 쓰면 되기 떄문에 확장에는 열려있고 수정에는 닫힌 구조가 되었다.
3. 리스코프 치환 원칙
파생클래스가 기본클래스를 대체할 수 있어야한다.
즉 하위 클래스는 어떠한 경우에도 부모 클래스를 대체할 수 있어야 한다.
ex: 자동차를 상속받아서 다양한 자동차를 만드는건 괜찮은데 자동차를 상속받아 놓고는 비행기를 만들면 안된다.
해결법
추상클래스를 조금 더 간단하게
만들고 조금 더 분류를 해서 만들고
상속을 쓰는 것보다는 인터페이스를 생성해서 여러 인터페이스를 조합하는 것이 좋을 수 있다.
단순하게 탈것 클래스를 만드는 것이 아니라
도로를 달리는 탈것 / 레일을 달리는 탈 것을 나눠서 클래스를 만들고
해당 클래스로부터 파생된 자동차 클래스와 기차 클래스를 만드는게 바람직하다.
4. 인터페이스 분리 원칙
인터페이스를 사용할떄 한번에 크게 사용하지 말고 작은 단위로 나눠서 사용하라는 원칙.
예를 들어서 유닛 스테이터스 인터페이스가 있다면 이렇게 모든 것을 넣지 말고 최대한 나눠야한다.
이동 인터페이스,데미지 인터페이스, 스텟 인터페이스 이런식으로 만들고
이런 인터페이스들 조합하는 식으로 코드를 발전시켜나가야한다.
이런식으로 나눈다면 움직임과,데미지와,스텟이 있는 일반 유닛,
움직임과 스텟은 있지만 데미지를 받지않는 무적 유닛을 생성할수있다.
ex:파괴되지 않고 움직이는 장해물
이런식으로 인터페이스를 조합해서 사용한다면 코드 간의 결합도가 낮아지고 수정이 용이해진다.
5.의존성 역전 원칙
고수준 모듈이 저수준 모듈에서 직접 가져오면 안된다.
알아듣기 쉽게 예시를하자면
Door 클래스에는 Open과 Close 기능이 존재한다
그리고 Switch라는 클래스에 문을 열고 닫게 만들어보자
그럼 Switch 클래스에서 Door를 연결하고 bool로 상태를 저장한 뒤에
Toggle이라는 메소드(함수)를 통해 문을 열고 닫는 기능을 구현한다
이렇게하면 스위치 클래스가 문을 열고 닫게되었지만
직접적인 문제는 Switch클래스가 Door라는 클래스를 직접적으로 알고있다
그렇기 떄문에 스위치 클래스는 Door만 열고 닫을 수 있다.
그런데 Switch라는 클래스는 문만 열고 닫을수 있는게 아니라
조명/함정 여러가지를 활성/비활성화를 할 수 있어야하는데
그럼 어떻게 설계를 해야 코드 수정 없이 확장성을 유지할 수 있을까?
스위치 기능을 인터페이스로 만들고 이 인터페이스에 활성/비활성하는 함수를 생성한다
그리고 Door에 이 인터페이스를 써주면 된다
그럼 기존의 스위치 클래스는 Door를 직접적으로 연결하는게 아니라
인터페이스를 통해서 연결을 하고
Toogle메소드(함수)를 통해 인터페이스에 메소드를 사용하면 된다.
이렇게 되면 기존에는 Door만 여닫을수 있엇지만
이제는 스위치 인터페이스를 가지고 있는 모든 객체를 활성/비활성 할 수 있게 되었다.
결론적으로 특정 클래스에 직접적으로 의존하는게 아니라
인터페이스를 거쳐서 사용하기 떄문에 느슨한 결합이 이뤄지게 된다.
이것이 바로 의존성 역전 원칙이다.