좋은 객체 지향 설계의 5가지 원칙 SOLID
클린 코드로 유명한 로버트 마틴이 좋은 객체지향 설계의 5가지 원칙을 정리한 것이다.
SRP 단일 책임 원칙 (Single Reonsibility Principle)
한 클래스는 하나의 책임만 가져야 한다.
하나의 클래스는 하나의 책임만 가져야 한다. 이때 하나의 책임이라는 것은 클 수도 있고 작을 수도 있고 모호한데 이것은 문맥과 상황에 따라 잘 결정해야 한다. 중요한 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이다.
예를 들면, UI의 변경이나 객체 생성과 사용을 분리하는 것 등이 있다.
OCP 개방 폐쇄 원칙 (Open Closed Principle)
- 소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀 있어야 한다.
- 인터페이스를 구현한 새로운 클래스를 만들어서 새로운 기능을 구현한다. (다형성을 이용하고, 기존 코드를 변경하지 않도록 한다.)
- 역할과 구현의 분리를 생각하면 된다.
MemberService라는 객체가 있고 서비스의 저장을 메모리에 할지 DB에 저장할지를 구현하기 위해 구현체인 MemberService에서 다음과 같이 코드를 수정했다고 가정해보자.
MemberRepository m = new MemoryMemberRepository(); // 변경 전
MemberRepository m = new JdbcMemberRepository(); // 변경 후
여기서의 문제점은 구현객체를 변경하기 위해 클라이언트 코드를 변경하였다는 점이다. 저장소를 구현할 때 MemberRepository라는 인터페이스를 이용해서 다형성을 사용했지만 OCP원칙을 지킬 수 없었다. (클라이언트가 기존코드를 변경하지 않으면 안 됨) 이 문제를 객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자가 필요하다. 스프링에서는 스프링 컨테이너가 그 역할을 해준다.
LSP 리스코프 치환 원칙 (Liskov Substitution Principle)
- 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
- 다형성에서 하위클래스는 인터페이스 규약을 지켜야 한다.
- 단순 컴파일을 말하는 것이 아님
- 예를 들어 자동차 인터페이스의 엑셀의 앞으로 가라는 기능에서 뒤로 가도록 구현하면 LSP원칙을 적용한 것이다.
ISP 인터페이스 분리 원칙(Interface Segregate Principle)
- 특정 클라이언트를 위한 인터페이스 여러 개가 하나의 범용 인터페이스보다 낫다.
- 자동차 인터페이스 → 운전 인터페이스, 정비 인터페이스
- 사용자 클라이언트 → 운전자 인터페이스, 정비사 인터페이스
- 분리하면 정비 인터페이스가 변했을 때 정비사 인터페이스만 바꾸면 된다.
- 기능에 맞게 적당하게 쪼개는 것이 중요하다.
DIP 의존관계 역전 원칙(Dependency Inversion Principle)
- 프로그래머는 추상화에 의존해야지 구체화에 의존하면 안된다.
- 쉽게 말하면 클라이언트 코드가 구현 클래스를 바라보지 않고 인터페이스만 바라보라는 뜻이다.
- 역할에 의존해야지 구현에 의존하면 절대 안 된다. 클라이언트가 인터페이스 의존해야 유연하게 인터페이스를 변경할 수 있다.
위의 예시에서도
MemberRepository m = new MemoryMemberRepository();
와 같이 MemberService()라는 구현 클라이언트가 구현 클래스를 직접 선택하게 되면 DIP를 위반하는 것이다. 인터페이스에만 의존하도록 하자.
스프링은 다음과 같은 기술들로 다형성, OCP, DIP가 가능하도록 지원한다.
- DI, 의존관계 주입, 의존성 주입
- DI 컨테이너: 자바의 객체를 컨테이너에 넣어놓고 의존관계를 연결, 주입하는 것을 제공한다.
클라이언트 변경 없이 기능을 확장 할 수 있게 되어 부품교체하듯이 개발이 가능하다. 기능을 확장할 가능성이 없다면 구체클래스를 사용하고 향후 꼭 필요할 때 리팩터링 하는것이 좋다.