/* 이 글은 김영한님의 강의를 보고 정리하려고 작성한 글입니다. */
SOLID
* 클린코드로 유명한 로버트 마틴이 좋은 객체 지향 설계의 5가지 원칙을 정리
- SRP : 단일 책임 원칙 (Single Responsibility Principle)
- OCP : 개방-폐쇄 원칙 (Open / Closed Principle)
- LSP : 리스코프 치환 원칙 (Liskov Substitution Principle)
- ISP : 인터페이스 분리 원칙 (Interface Segregation Principle)
- DIP : 의존관계 역전 원칙 (Dependency Inversion Principe)
1. SRP (단일 책임 원칙)
◾ 한 클래스는 하나의 책임만 가져야 한다.
(여기서 하나의 책임이라는 것은 모호하기 때문에 범위의 단위를 적절히 잘 조절해야함.)
➔ 한 클래스가 데이터베이스 연결, 비즈니스 로직, 사용자 인터페이스 처리 등 다양한 책임을
가지고 있다면 이것은 SRP를 위반했다고 볼 수 있다.
◾ 중요한 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것
ex ) UI변경, 객체의 생성과 사용을 분리
➔ 특정 책임만 가질 경우 변경하거나 디버깅 하는 것이 쉬워지며 유지보수성을 향상시킴
2. OCP (개방-폐쇄 원칙)
◾ 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
➔ 이게 무슨 말인지 처음에 헷갈렸는데..
➔ 기존의 코드를 수정하지 않고도 새로운 기능을 추가하거나 변경할 수 있어야 한다.
◾ 아래의 예시처럼 인터페이스와 다형성을 사용하여 역할과 구현을 분리해야 한다.
◾ 역할과 구현의 분리 예시
➔ 새로운 클래스를 만들어 이미 정의된 인터페이스를 구현하거나 상속받아 새로운 기능을 구현
➔ 오버라이딩 하여 메서드를 재정의 하는 방법
3. LSP (리스코프 치환 원칙)
◾ 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
◾ 다형성에서 하위클래스는 인터페이스 규약을 다 지켜야 한다는 것
◾ 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체를 믿고 사용하려면 이 원칙이 필요하다.
◾ 단순히 컴파일에 성공하는 것을 넘어서는 이야기
◾ ex ) 자동차 인터페이스의 엑셀은 앞으로 가는 기능 일 때, 여기에 뒤로 가는 기능으로 만들 수도 있고 그렇게 하더라 도 컴파일 오류가 나진 않음. 그러나 LSP원칙은 컴파일 단계를 얘기하는것이 아님.
◾ 인터페이스의 엑셀 규약은 무조건 앞으로 가야되기 때문에 그 기능에 대해 보장을 해줘야 함.
➔ 뒤로 간다면 LSP원칙 위반
4. ISP (인터페이스 분리 원칙)
◾ 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
◾ ex ) 자동자 인터페이스 : 운전 인터페이스, 정비 인터페이스로 분리
사용자 클라이언트 : 운전자 클라이언트, 정비사 클라이언트로 분리
◾ 이렇게 분리 했을 때 정비와 관련된 기능을 바꿀 경우 정비 인터페이스와 정비사 인터페이스만 바꾸면 됨.
➔ 운전자 클라이언트에 영향을 주지 않음 !
◾ 인터페이스가 명확해지고, 대체 가능성이 높아진다.
5. DIP (의존관계 역전 원칙)
◾ 프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다"의 원칙을 따르는 방법 중 하나
➔ 구현 클래스에 의존하지말고, 인터페이스에 의존하라는 뜻 = 역할에 의존해야 한다.
➔ 원빈(=구현체)이 김태희(=구현체)랑만 공연 연습을 했다고해서 송혜교(=구현체)랑 공연하기 힘들면 안됨. 원빈은 로미오(=인터페이스) 역할에만 의존해야 함
상대배우(=구현체)에 의존하지 말고 자신의 대본(=역할, 인터페이스)에 의존해서 언제든지 다른 배우와 공연이 가능하도록 역할과 구현을 철저하게 분리해야 함.
결론
◾ 객체 지향의 핵심은 다형성
But, 다형성 만으로는 쉽게 부품을 갈아 끼우듯 개발할 수 없다.
(다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경된다.)
◾ 다형성 만으로는 OCP, DIP를 지킬 수 없다.
➔ 하지만 스프링을 사용한다면 이것들을 지킬 수 있게 된다.
다시 스프링으로
◾ 스프링은 다음기술로 다형성 + OCP, DIP를 가능하게 지원
- DI(Dependency Injection) : 의존관계, 의존성 주입
- DI 컨테이너 제공 = IOC
◾ 클라이언트 코드의 변경 없이 기능 확장
◾ 쉽게 부품을 교체하듯이 개발
정리
◾ 모든 설계에 역할과 구현을 분리하자.
ex ) 위에 나왔던 공연의 예시처럼 배역만 만들어두고 배우는 언제든지 유연하게 변경할 수 있도록 설계
◾ 이상적으로는 모든 설계에 인터페이스를 부여하자
(인터페이스를 먼저 설계하고 구현을 나중에 정하게 되면 구현 기술이 바뀌더라도 변경 범위가 작고 유연해짐.)
But, 인터페이스를 도입하면 추상화라는 비용이 발생함.
➔ 기능을 확장할 가능성이 없다면 구체 클래스를 직접 사용하고, 향후 꼭 필요할 때 리팩터링해서 도입하는 것도 방법.
◾ 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
◾ 다형성에서 하위클래스는 인터페이스 규약을 다 지켜야 한다는 것, 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체는 믿고 사용하려면 이 원칙이 필요하다.
◾ 단순히 컴파일에 성공하는 것을 넘어서는 이야기
ex ) 자동차 인터페이스의 엑셀은 앞으로 가는 기능 일 때, 여기에 뒤로 가는 기능으로 만들 수도 있고 그렇게 하더라도 컴파일 오류가 나진 않음. 그러나 LSP원칙은 컴파일 단계를 얘기하는것이 아님.
인터페이스의 엑셀 규약은 무조건 앞으로 가야되기 때문에 그 기능에 대해 보장을 해줘야 함.
➔ 뒤로 간다면 LSP원칙 위반
'Spring' 카테고리의 다른 글
[스프링 핵심 원리 - 기본편] #3 인프런 강의 정리(예제만들기 - 회원 도메인 설계, 개발, 실행, 테스트) (1) | 2024.06.11 |
---|---|
[스프링 핵심 원리 - 기본편] #2 인프런 강의 정리(예제만들기 - 프로젝트 생성, 비즈니스 요구사항과 설계) (0) | 2024.06.10 |
상속 추상화, 캡슐화, 다형성 개념 (0) | 2024.03.29 |
서블릿(Servlet)이란? (JSP(Java Server Page)) (0) | 2023.03.02 |
Spring MVC 4 - MVC 프레임워크 만들기 (0) | 2023.02.27 |