객체지향 5원칙(SOLID)

객체 지향 프로그래밍의 다섯 가지 기본 원칙

  1. 단일 책임 원칙(Single responsibility principle) : SRP
  2. 개방 폐쇄 원칙(Open/closed principle) : OCP
  3. 리스코프 치환 원칙(Liskov substitution principle) : LSP
  4. 인터페이스 분리 원칙(Interface segregation principle) : ISP
  5. 의존관계 역전 원칙(Dependency inversion principle) : DIP

 

SOLID 객체 지향 원칙을 적용하면 코드를 확장하고 유지 보수 관리하기가 더 쉬워지며, 불필요한 복잡성을 제거해 리팩토링에 소요되는 시간을 줄임으로써 프로젝트 개발의 생산성을 높일 수 있다.

 

SOLID는 프레임워크도 라이브러리의 패턴도 아니며, 특정 기술에 국한되지 않는다.

 

5가지 원칙들은 서로 독립된 개별적인 개념이 아니라 서로 개념적으로 연관 되어 있다. 원칙 끼리 서로가 서로를 이용하기도 하고 포함하기도 한다.

 

 

1. 단일 책임의 원칙 : SRP (Single Responsibility Principle)

모든 서비스는 단 하나의 책임을 수행하는 데 집중되어야 한다.

책임이라는 의미는 하나의 기능 담당으로 보면 된다.

SRP 원칙을 적용하면 다른 클래스들이 서로 영향을 미치는 연쇄작용을 줄일 수 있다.

응집도(cohesion)는 높이고 결합도(coupling)은 낮출 수 있다. 책임을 적절하게 분배함으로써 코드의 가독성 향상, 유지보수 용이라는 이점까지 누릴 수 있고 다른 원칙들을 적용하는 기초가 된다.

변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이다.

 

 

 

 

2. 개방폐쇄의 원칙 : OCP (Open Close Principle)

소프트웨어의 모든 구성요소(클래스, 모듈, 함수)는 확장에는 열려있고, 변경에는 닫혀있어야한다.

  • 확장에 열려있다 : 새로운 변경 사항이 발생했을 때 유연하게 코드를 추가함으로써 큰 힘을 들이지 않고 애플리케이션의 기능을 확장할 수 있음
  • 변경에 닫혀있다 : 새로운 변경 사항이 발생했을 때 객체를 직접적으로 수정을 제한함.

기능 추가 요청이 오면 클래스를 확장을 통해 손쉽게 구현하면서 확장에 따른 클래스 수정은 최소화 하도록 프로그램을 작성해야 하는 설계 기법이다.

요구사항의 변경이나 추가사항의 발생하더라도, 기존 구성요소는 수정이 일어나지 말아야하며 쉽게 확장이 가능하여 재사용할 수 있어야 한다.

추상화(Abstraction)와 다형성(Polymorphism)로 OCP를 가능하게 한다.

클래스를 설계할 때 변할 부분과 변하지 않을 부분을 명확히 구분해야 한다.

역할과 구현을 분리한다.

 

 

 

 

3. 리스코브 치환의 원칙 : LSP (the Liskov Substitution Principle)

프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다.

서브 타입은 언제나 기반(부모) 타입으로 교체할 수 있어야 한다.

부모 클래스를 상속한 자식 클래스는 부모 클래스의 역할을 정확히 해내야한다.

상속의 과정 중 메소드의 재정의가 필요하다면 현재 자식 클래스가 부모 클래스의 기존 메소드의 의미를 해치지는 않는지 신중히 고민하고 올바르게 상속해야 한다.

 

 

 

 

4. 인터페이스 분리의 원칙 : ISP (Interface Segregation Principle)

특정 클라이언트를 위한 범용 인터페이스 하나보다 인터페이스 여러개가 낫다.

인터페이스를 구체적이고 작은 단위들로 분리시켜 꼭 필요한 인터페이스만 상속해야 한다. SRP가 클래스의 단일책임을 강조했다면 ISP는 인터페이스의 단일책임을 강조한다.

 

 

 

 

5. 의존성 역전의 원칙 : DIP (Dependency Inversion Principle)

상위 모듈은 하위 모듈에 의존해서는 안된다. 둘 다 추상화에 의존해야한다.

추상화는 구체적인 것에 의존해서는 안된다. 구체적인 것은 추상화에 의존해야한다.

의존 관계가 존재하되, 구체적인 클래스에 의존하지 말고 최대한 추상화한 클래스에 의존해야 한다. 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다.