Object-Oriented Programming (OOP)를 더 유연하게 하고, 이해하기 쉽게 하기 위한 접근법으로 SOLID라는 객체지향의 설계 원칙이 있는데 본격적으로 SPRING 수업에 들어가기 전에 한번 정리를 해보려 한다.
SOLID는 유명한 책인 '클린 코드'의 저자인 Robert C. Martin (Uncle BOB Martin..)이 00년도에 그의 논문 "Design Principles and Design Patterns"에 처음으로 소개된 5가지의 원칙이다. SOLID원칙을 잘 지킨다면 변경이 용이하고 유지보수와 확장이 쉬운 소프트웨어를 개발하는데 도움이 된다고 한다.
어떻게 하면 객체지향을 더 잘 쓸 수 있을까? 하는 관점에 의미를 두고 정리해보자.
Single responsibility
단일 책임의 원칙 SRP
"There should never be more than one reason for a class to change"
클래스가 변경되는 이유가 하나여야 한다는 점에서 하나의 클래스는 하나의 책임을 갖는다고 정의한다. 여기에서 책임은 해당 클래스가 하는 '기능'을 의미한다.
간단하게 요약하자면 하나의 기능에 대해 높은 응집도를 갖되, 다른 기능들과는 낮은 결합도를 가져야 한다.
단일 책임 원칙을 잘 지켰다면, 만약 변경이 필요한 상황이 오더라도 명확하게 수정할 대상을 확인할 수 있다. 따라서
적절하게 기능을 분리해서 서로 영향을 받지 않게 추상화를 함으로써 변화에 손쉽게 대처할 수 있다.
Open-closed
개방-폐쇄 원칙 (가장 중요한 개념) OCP
"Software entities (classes, modules, functions, etc) should be open for extension, but closed for modification"
클래스, 모듈, 함수등과 같은 소프트웨어 개체는 확장에는 열려있어야 하고, 수정은 할 수 없어야 한다.
즉,
- 기존의 코드를 변경하지 않으면서 (closed for modification)
- 기능을 추가 할 수 있도록 (open for extension) 설계해야 한다.
대표적으로 개방-폐쇄 원칙을 지킨 것이 있는데 바로 "인터페이스"이다. 인터페이스에는 최소한의 기능 구현을 통해 타 클래스나 인터페이스에서 구현을 통해 (Implement & method overriding) 사용하게 되는데 이때 인터페이스의 기존 코드를 변경하지 않으면서, 구현을 통해 기능을 추가할 수 있다.
한계점으로, 분명히 다형성이 적용되어 있지만, 기능의 변경을 위해 코드를 변경해야 하는 경우가 있다.
예를들어 인터페이스 MemberRepository를 구현한 객체 Memory - jdbc가 각각 있다고 했을때,
Memory의 기능을 하는 녀석을 jdbc의 기능으로 바꾸고 싶다면, 코드를 변경해야 하는 상황이 온다.
MemberRepository repository = new MemoryMemberRepository(); //기존 코드
MemberRepository repository = new jdbcMemberRepository(); // 변경 코드
이때 객체를 생성하고 관계를 맺어주는 별도의 설정자가 필요한데 이는 스프링 컨테이너가 처리해준다.
추가로 위의 클래스는 MemberRepository도 의존하고 Memory - jdbc도 의존하는 상태이기 때문에
DIP도 위반한 케이스가 된다.
Liskov substitution
리스코프 치환 원칙 LSP
"Functions that use pointers or references to base classes must be able to use object of derived classes
without knowing it"
기준 클래스를 포인터나 참조를 통하여 사용하는 경우, 그 하위 클래스는 상위 클래스의 내용을 알지 못한 상태여도 상위 클래스를 사용할 수 있다.
요약하면 하위 클래스는 상위 클래스를 대체할 수 있어야 한다.
즉 상위 클래스 대신에 하위 클래스를 넣어도 잘 작동해야 한다. 그리고 하위 클래스가 상위 클래스의 책임을 무시하거나 재정의 하지 않고 확장만 수행할 수 있어야 한다.
하위 클래스가 상위 클래스의 기능(책임)을 그대로 받아오면서 추가적인 기능을 정의하고 싶을 때 상속을 해야한다.
Interface segregation
인터페이스 분리 원칙 ISP
"Many client-specific interfaces are better than one general-purpose interface"
특정 클라이언트를 위한 여러 개의 구체적인 인터페이스가 하나의 범용적인 인터페이스보다 낫다.
인터페이스 분리 원칙에는 클라이언트에서는 "클라이언트 자신이 사용하지 않는 기능으로부터 영향을 받지 않아야 한다"는 내용이 있다.
그리고 큰 덩치의 인터페이스를 구체적이고 작은 단위로 쪼개서 클라이언트가 필요한 특정 인터페이스만 골라서 사용할 수 있게 한다.
SRP가 클래스의 단일 책임을 중요시 한다면, ISP는 인터페이스의 단일 책임을 중요시 한다.
Dependency inversion
의존관계 역전 원칙 DIP
"Depend upon abstractions, NOT concretions."
추상화(변화하기 어려운것)에 의존해야 한다. 구체화(변화하기 쉬운 것)에 의존하면 안 된다.
구체화 (변화하기 쉬운 것)은 구체화된 클래스를 말하고 추상화(변화하기 어려운 것)는 추상 클래스나 인터페이스를 말한다. 따라서 정리하면 의존관계를 맺을 때 구체적인 클래스보다 인터페이스나 추상 클래스와 관계를 맺는 것을 의미한다.
결론: interface 많이 써라
뒤의 Spring에서 나오는 의존성 주입 부분이 이 DI 원칙을 따르는 방법 중에 하나이다.
'programming > JAVA' 카테고리의 다른 글
[TBC]Java - 자료구조 (Data Structure) (0) | 2022.05.26 |
---|---|
Java - 재귀 (0) | 2022.05.25 |
Java - Effective cont. (0) | 2022.05.20 |
Java - Effective (0) | 2022.05.19 |
Java - Inner Class (0) | 2022.05.18 |