객체지향의 설계원칙은 객체지향 프로그래밍에서 가장 중요한 코드의 유연성과 확장성 을 높이기 위해 만들어졌다.
코드의 유연성과 확장성을 높이기 위해 코드의 구조를 명확하게 설계해야 하고, 각 클래스와 모듈 간 관계를 잘 조절해야 하기 떄문에 만들어졌고.. 이를 따르면 코드의 중복을 최소화하고 의존성을 추상화하여 코드의 결합도를 낮출 수 있다.
또한, SOLID 원칙은 객체지향 설계의 기본 원리들을 추상화 하여 코드를 더욱 일관성 있고 예측 가능하게 만들어준다.
즉, SOLID 원칙은 코드의 유지보수성, 확장성, 재사용성, 가독성, 유연성 등을 높여주는 효과가 있다.
그럼으로, SOLID 원칙은 객체지향 개발에서 반드시 따라야할 중요한 가이드라인 이라고 할 수 있다.
이러한 SOLID 원칙은 다음과 같다.
- SRP (Single Responsibility Principle) - 단일 책임 원칙
- OCP (Open-Closed Principle) - 개방 폐쇄 원칙
- LSP (Liskov Substitution Principle) - 리스코프 치환 원칙
- ISP (Interface Segregation Principle) - 인터페이스 분리 원칙
- DIP (Dependency Inversion Principle) - 의존 역전 원칙
밑은 각 항목을 설명한다.
SRP ( Single Responsibility Principle ) -단일 책임 원칙
하나의 클래스는 하나의 책임만 가져야 한다.
즉, 클래스는 한가지의 역활만 하게 되어야 한다는 것이다.
한 클래스는 여러개의 책임을 가지게 되면 코드의 복잡성이 증가하고 유지보수가 어려워진다. SRP는 이러한 문제를 해결하기 위해 각 클래스가 담당하는 책임을 명확하게 구분하고, 한 클래스가 변경되어야 하는 이유는 오직 한가지 이유여야 한다는 원칙을 가리킨다.
SRP는 코드의 응집성을 높이는데 도움을 준다.
응집성이란 클래스나 모듈 내부에서 함께 처리되는 기능들의 관련성을 의미한다. SRP를 따르면 한 클래스 내부에서는 서로 관련된 기능만을 처리함으로 응집성이 높아지고( 코드간의 결과물을 위한 내부연산처리 가 효율적으로 변화하고) 코드의 가독성과 이해도가 높아지게 된다.
예를들어보자. 주문관리 시스템이 있다고 가정하면, 주문을 추가하고 삭제하는 기능과 결제기능을 모두 한 클래스로 만들경우. 이 클래스는 주문관리, 결제관리 로 두가지의 책임을 가지게 됨으로 SRP를 위반하게 된다.
이를 해결하기 위해 주문관리와 결재기능을 각각 다른 클래스로 분리하면, 각 클래스는 단일 책임을 가지게 되어 코드의 구조가 명확해지고, 유지보수성이 향상된다. 또한 각 클래스를 다른 코드로 이식하는 것 에도 큰 어려움이 없어질 것 이다.
나중에 결제 또는 주문관리 하는 그 코드가 다른부분에 필요할 수 도 있기 때문이다.
OCP ( Open - Closed Principle ) - 개방 폐쇄 원칙
확장에는 열려있고 변경에는 닫혀있어야 한다.
즉, 클래스는 기존의 코드를 수정하지 않고도 새로운 요구사항에 대처할 수 있도록 설계되어야 한다는 것이다.
OCP를 따르면, 새로운 요구사항이 생겨도 기존 코드를 변경하지 않고도 새로운 기능을 추가할 수 있다. 이를 통해 코드의 재사용성과 확장성을 높일 수 있다.
또한, 코드를 수정하지 않고도 새로운 기능을 추가할 수 있으므로, 코드의 안정성과 신뢰성이 향상되는 효과가 있다.
예를 들어보자. 특정 클래스가 새로운 기능을 추가해야 할 경우, 이를 위해 기존 클래스의 코드를 수정하면 기존 코드와 새로운 코드가 결합되어 코드의 복잡성이 증가하고 유지보수가 어려워지게 된다.
하지만, OCP를 따르면 새로운 기능을 추가하기 위해 기존 클래스의 코드를 수정하는 대신, 새로운 클래스를 만들어 기존 클래스를 확장하여 새로운 기능을 추가할 수 있게 된다.
기존의 클래스의 코드를 수정하게 되면. 알 수 없는 오류가 나타날 수 있고, 또한 기존의 수정해야하는 코드가 내가 작성하지 않은 코드 일 수 도 있기 때문이다.
내가 작성한 코드도 알아보기가 힘든데, 다른사람이 작성한 코드는 얼마나 알아보기 어렵겠는가!
LSP ( Liskov Subsitution Principle ) - 리스코프 치환 원칙
자식 클래스는 부모 클래스의 대체 가능성을 보장해야한다.
즉, 어떤 클래스의 인스턴스가 있을 때, 이 클래스의 자식 클래스의 인스턴스로 대체하여 사용할 수 있어야 한다는 것 이다.
LSP를 따르면, 부모 클래스와 자식 클래스는 같은 인터페이스를 공유하며, 자식 클래스는 부모 클래스에서 정의된 기능을 오버라이딩 하지 않으면서 추가적인 기능을 제공할 수 있다. 이를통해 코드의 재사용성과 확장성을 높일 수 있다.
이러한 LSP를 따르지 않으면 코드의 일관성과 예측 가능성이 떨어지게되어 코드의 안정성이 감소할 수 있다.
자식클래스에서 부모 클래스의 메서드를 오버라이딩 하면서 부모 클래스에서 정의된 사전 조건(pre-condition) 이나 사후조건(post - condition) 을 변경할 경우, 코드의 예측 가능성이 떨어지고 버그가 발생할 가능성이 높아진다.
LSP를 지키기 위해서 자식 클래스가 부모 클래스의 대체 가능성을 보장하도록 설계해야한다. 자식 클래스는 부모 클래스의 기능을 확장할 수 있지만, 기존의 기능을 변경하거나 사전/사후 조건을 변경해서는 안된다.
작업을 분할처리 하고 있는데, 컴퓨터 한개에 문재가 생긴다고 작업 전체가 정지되면 얼마나 화나겠는가!
ISP ( Interface Segregation Principle ) - 리스코프 치환 원칙
클라이언트는 자신이 사용하지 않는 메서드에 의존하지 말아야한다.
즉, 인터페이스는 클라이언트가 필요로 하는 최소한의 메서드 만을 포함해야 한다는 것이다.
이를 통해 인터페이스를 작고 독립적인 단위로 분리하여, 하나의 클래스나 모듈이 여러 개의 인터페이스를 구현할 수 있도록 하는 것이 목표이다.
ISP를 따르면, 하나의 인터페이스에 너무 많은 메서드가 들어가지 않도록 하여, 해당 인터페이스를 구현하는 클래스는 자신이 필요로 하는 메서드만 구현하면 되도록 해야한다. 이를 통해 코드의 중복을 최소화하고, 의존성을 추상화 하여 코드의 결합도를 낮출 수 있다.
예를 들자면, 특정 클래스에서 인터페이스의 모든 메서드를 사용하지 않는 경우, 이 인터페이스를 구현하도록 강제하는 것은 ISP를 위반하는 것 이다. 하나의 클래스가 각기 다른 클래스의 인터페이스를 모두 담당하지 않도록 해야한다.
이를 해결하기 위해 인터페이스를 세분화하여 해당 클래스가 필요로 하는 메서드만을 포함하는 작은 인터페이스들로 분리하여 구현해야 한다. 이를 통해 코드의 유지보수성과 재사용성을 높일 수 있다.
이는 쉽게말하면 여러개를 다 할줄아는 사람보다 한개만 집중적으로 투자한 사람이 그 작업에서는 더 낫다. 라는것이다.
DIP ( Dependency Inversion Principle ) - 의존 역전 원칙
추상화된 것은 구체적인 것에 의존하면 안된다.
즉, 의존관계를 역전시켜서 고수준의 모듈이 저수준 모듈에 의존하도록 만들어야 한다는 것 이다.
이를 위해 DIP는 추상화된 인터페이스를 통해 의존성을 주입하도록 한다.
DIP를 따르면, 구체적인 구현이 아닌 추상회된 인터페이스에 의존함으로, 코드의 결합도를 낮출 수 있다. 이를통해 코드의 유지보수성과 확장성을 높일 수 있다.
예를들어, 특정 크래스에서 다른 클래스의 구현에 직접 의존하는 경우, 이는 DIP를 위반하는 것이다. 이를 해결하기 위해, 인터페이스를 정의하고 해당 인터페이스를 구현하는 클래스를 주입받도록 변경하면 된다. 이를통해 클래스간의 의존성을 역전시키고 인터페이스를 통해 의존성을 주입함으로써 코드의 유연성과 확장성을 높일 수 있다.
일상생활을 예시로 들어보자.
차를 운전하다가 차의 기름을 넣는 상황이라고 가정해보자. 우리는 자동차에 기름을 넣을때 기름이 어떻게 들어가는지는 잘 알지 못한다. 주유소 기계를 잘 아는사람이 아니고서야, 자동차에 기름을 넣을 때 펌프가 어떤식으로 동작하는지, 자동차의 연료캡이 어떤식으로 잠기게 되는지, 연료캡의 나사산 갯수는 몃개인가. 주유기 내부의 기계가 어떤식으로 동작하는가 등의 자세한 정보를 알지 못해도, 자동차에 기름을 넣을 수 있다.
마찬가지로, 자동차를 운전할 때 에도 자동차를 잘 알지 못하는 사람은 자동차가 어떤식으로 동력을 얻고 동력을 전달하고 바퀴가 굴러가며, 왜 시동이 켜지는지를 알지 못하지만, 자동차를 운전하는법 만 알아도 운전할 수 있다.
즉, 구체적인 작업에 대한 지식이나 경험에 의존하지 않고서도 핸들과 브레이크,엑셀 등 자동차 자체의 지식에 비하면 매우 적은 정보량 만으로도 자동차를 운전할 수 있으며, 이는 다르게 설명하자면,
우리는 자동차 의 시스템을 통해 추상화된 인터페이스를 제공받음으로써 자동차를 운전할 수 있는것이다.
객체지향에서도 이와 비슷한 개념으로 클래스간의 의존성을 추상화된 인터페이스를 통해 주입하면, 위에 설명한것과 같이
구체적인 구현에 의존하지 않으면서도 코드의 유지보수성과 확장성을 크게 높여줄 수 있다.
위 내용을 보기좋게 축약해보면 다음과 같다.
- SRP (Single Responsibility Principle) - 단일 책임 원칙
- 하나의 클래스는 하나의 책임만 가져야 한다.
- 코드의 응집성이 향상된다.
- 클래스 간의 의존성이 줄어든다.
- 위 항목에 의거하여 코드의 가독성과 유지보수성이 향상된다.
- 하나의 클래스는 하나의 책임만 가져야 한다.
- OCP (Open-Closed Principle) - 개방 폐쇄 원칙
- 확장에는 열려있고 변경에는 닫혀있어야 한다.
- 코드의 재사용성과 확장성이 높아진다.
- 확장에는 열려있고 변경에는 닫혀있어야 한다.
- LSP (Liskov Substitution Principle) - 리스코프 치환 원칙
- 자식 클래스는 부모 클래스에서 가능한 행위를 수행할 수 있어야 한다.
- 코드의 일관성이 높아진다.
- 코드의 예측 가능성이 높아진다.
- 코드의 안정성과 신뢰성이 높아진다.
- 자식 클래스는 부모 클래스에서 가능한 행위를 수행할 수 있어야 한다.
- ISP (Interface Segregation Principle) - 인터페이스 분리 원칙
- 인터페이스는 가능 한 한가지의 인터페이스만 제공하도록 분리해야한다.
- 코드의 중복을 최소화 한다.
- 의존성을 추상화하여 코드의 결합도를 낮춘다
- 인터페이스는 가능 한 한가지의 인터페이스만 제공하도록 분리해야한다.
- DIP (Dependency Inversion Principle) - 의존 역전 원칙
- 추상화에 의존해야 하며, 구체화에 의존해서는 안 된다.
- 코드의 유지보수성이 높아진다.
- 코드의 확장성이 높아진다.
- 추상화에 의존해야 하며, 구체화에 의존해서는 안 된다.
'컴퓨터 용어 정리' 카테고리의 다른 글
C++ algorithm 라이브러리 (0) | 2023.05.05 |
---|---|
디자인 패턴(Design Pattern) (0) | 2023.04.28 |
C++ mutable (0) | 2023.04.26 |
C++ Inline (0) | 2023.04.26 |
C++ 생성자(constructor) (0) | 2023.04.22 |