좋은 객체 지향 셜계의 5가지 원칙
SOLID 원칙이란?
클린 코드로 유명한 로버튼 마틴이 2000년대 초반 객체 지향 프로그래밍 및 설계의 5가지 원칙을 정리한 내용이다. 프로그래머가 시간이 지나도 유지 보수와 확장을 쉽게 하도록 만들어진 지침으로 각 원칙의 앞 글자를 따서 SOLID 원칙이라고 부른다.
- SRP
- OCP
- LSP
- ISP
- DIP
SRP
Single Responsibility Principle
“한 클래스는 하나의 책임만 가져야 한다”
객체 지향에서 책임이란 객체가 할 수 있는 것과 해야하는 것이다.
하나의 클래스가 여러 책임이 있다면 다른 객체들과 결합이 많아지고 변화가 생겼을 때 결합된 다른 객체들까지 변경 대상이 되기 때문에 유지 보수 하기가 매우 까다롭다. 기능별로 여러 클래스를 쪼개어 관리한다면 변경에 있어 유연하게 대처할 수 있는 장점이 생긴다.
결론은 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이다.
OCP
Open/Closed Principle
“소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀 있어야 한다”
기존의 코드를 변경하지 않으면서 새로운 기능을 구현할 수 있게 설계 해야한다.
- 추상화 기능은 같더라도 동작하는 자세한 로직은 다를 수 있다. 인터페이스를 통해 추상화함으로써 기존 코드를 변경하지 않고도 확장 가능하다.
- 상속 오버라이딩을 통해 구현 가능하다.
LSP
Liskov Substitution Principle
“프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다”
상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
리스코프 치환 법칙에 위배되지 않을때 상속 관계를 정의 해야한다. 또한 LSP를 만족하려고 OCP를 위배할 가능성도 있으니 주의가 필요하다.
LSP는 기능의 명세(계약)에 대한 내용이다. 기능 실행의 계약과 관련하여 흔히 발생하는 위반 사례는 다음과 같다.
- 명시된 명세에서 벗어난 값을 리턴
- 명시된 명세에서 벗어난 Exception 발생
- 명시된 명세에서 벗어난 기능 수행
ISP
Interface Segregation Principle
“특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다”
클라이언트에서는 클라이언트 자신이 사용하지 않는 기능에 대해서는 영향을 받으면 안된다. 즉, 인터페이스를 클라이언트에 특화 되도록 분리하는 설계 원칙이라고 할 수 있다. 클라이언트들은 분리된 작은 인터페이스중에서 필요한 기능만 골라서 사용하기 때문에 내부 의존성을 약화시켜 리팩토링, 유지 보수, 수정, 재배포등을 쉽게 할 수 있는 장점이 있다.
DIP
Dependency Inversion Principle
“프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다”
구현 클래스에 의존하지말고, 인터페이스에 의존하라는 뜻으로 객체간의 의존 관계를 맺을때 변화하기 어려운 것, 거의 변화가 없는 것과 의존하라는 원칙이다. 정책이나 전략 같은 큰 개념들이 변화하기 어려운 것에 포함되고 구체적인 방식, 사물이 변화하기 쉬운 것에 해당한다.
- 상위 모듈은 하위 모듈에 의존해서는 안된다. 상위 모듈과 하위 모듈 모두 추상화에 의존해야 한다.
- 추상화는 세부 사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야 한다.
다형성 만으로는 OCP, DIP를 지킬수 없다.
그러면 어떻게 해야하나? 바로 스프링이 다형성 + OCP, DIP를 가능하게 지원한다.
- DI: 의존관계 주입
- DI 컨테이너 제공
- 클라이언트 코드의 변경 없이 기능 확장
정리
모든 설계에 역할과 구현을 분리해야한다.
애플리케이션 설계도 공연을 설계 하듯이 배역(역할)만 만들어두고, 배우(구현)는 언제든지 유연하게 변경할 수 있도록 만드는 것이 좋은 객체 지향 설계이다.
모든 설계에 인터페이스를 부여하는 것이 이상적이다.