처음 자바언어에 대해 공부할때 객체지향을 이해하는데 많은 어려움이 있었습니다. 현재까지도 객체지향 프로그래밍에 대해 정확히 이해하지는 못한것 같습니다 ㅎㅎ
어찌저찌 자바문법을 떼고 스프링 프레임워크 공부를 시작했을때 DI, Ioc 등의 낯선 개념에 막막함과 좌절감을 맛봤던 기억이 납니다.
이번 포스팅은 당시에 모르고 넘어갔던 내용들에 대해 다시 공부하면서 알게된 점을 위주로 작성해보려고 합니다.
먼저 객체지향 프로그래밍이 왜 중요한지, 기업과 선배 개발자 분들이 왜 객체지향 프로그래밍을 강조하는지 알아야합니다.
객체지향 프로그래밍은 프로그램의 기능이 아닌 객체가 중심이 되어 "누가 어떤 일을 할 것인가?, 즉 어떤 클래스가 어떤 역할을 할 것인가?" 가 핵심이 됩니다. 객체 각각의 역할을 정의해 나가는것에 집중하는 것입니다.
반대로 절차지향 프로그래밍은 프로그램의 기능이 핵심이 됩니다. "무엇을 어떤 절차로 할 것인가?" 즉 어떤 기능을 어떤 순서로 처리하는가에 집중하게 됩니다.
유연하고 변경이 용이하다?
우리는 객체지향 핵심 개념이 "캡슐화, 상속, 추상화, 다형성" 이라는 것을 이미 알고있습니다.
스프링 프레임워크는 이 네가지 개념중 다형성을 극대화해서 개발할 수 있게 도와줍니다. 제어의 역전(IoC), 의존관게 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 분리할 수 있도록 지원합니다.
한 클래스는 하나의 역할만 가져야합니다.
하나의 역할이라는 것은 모호합니다. (역할이 클 수도 있고, 작을 수도 있다)
역할을 정할 때 중요한 기준은 변경입니다.
어떠한 변경이 있을 때, 파급효과(사이트 이팩트)가 적으면 단일 책임 원칙을 잘 지켰다고 할 수 있습니다.
소프트웨어 요소는 확장에는 열려있고 변경에는 닫혀있어야 합니다.
어려운 말이지만 다형성을 활용하면 쉽게 적용할 수 있습니다.
인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현합니다.
이는 기존 클래스를 변경하는 것이 아니고 새로운 클래스를 하나 만드는 것이기 때문에 확장에는 열려있고 변경에는 닫혀있어야 한다는 OCP 원칙을 잘 지켰다고 할 수 있습니다.
아래 코드는 구현체를 만들고 적용을 한다고 가정해보겠습니다.
실제 개발할 땐 new 연산자의 사용을 최대한 줄이고 DI 받아 객체를 사용하는 것이 좋습니다.
public class MemberService{
MemberRepository m = new MemberRepository(); // 기존코드
MemberRepository m = new JpaMemberRepository(); // 변경코드
}
개발 시점에 H2 데이터베이스에 데이터를 저장하는 기존 로직에서 실제 데이터베이스(MySql, Oracle) 등으로 코드를 변경하는 상황이라고 가정하겠습니다. 구현 객체를 변경하려면
MemberService
클래스의 기존 코드를 변경해야 합니다.
분명 인터페이스(MemberRepository
)를 구현한 새로운 클래스(JpaMemberRepository
)을 만들어 다형성을 사용했지만 OCP 원칙을 지킬수 없습니다.
즉 클라이언트가 코드를 변경해야 하는 상황입니다.
이러한 문제를 해결하기 위해서 객체를 생성하고 연관관계를 맺어주는 별도의 설정자가 필요합니다.
이 별도의 설정자는 바로 스프링컨테이너입니다. 결과적으로 OCP 원칙을 지키기 위해 DI와 IoC가 필요한 것이죠
❎ 위 코드는 MeberService 클래스가 구현클래스(JpaMemberRepository()
)를 알고 있기 때문에 DIP 원칙을 위배하고 있습니다. 아래 DIP 원칙에서 추가적으로 설명하겠습니다.
쉽게 이야기하면 클라이언트가 구현 클래스를 바라보지 않고 인터페이스만 바라보게 하라는 원칙입니다.
public class MemberService{
MemberRepository m = new JpaMemberRepository(); // 변경코드
}
위 코드에서 MemberSevice
가 MemberRepository()
또는 JpaMemberRepository()
등의 구현 클래스를 바라보는 것이 아닌 인터페이스를 바라보게 해야한다는 것입니다.
MemberRepository
)에 의존하지만 동시에 구현클래스(JpaMemberRepository
)에도 의존하고 있습니다.MemberRepository m = new JpaMemberRepository();
)클라이언트(MemberService
)는 인터페이스(MemberRepository
)에만 의존하도록 설계를 해야합니다.
IoC, DI 등을 이용하면 추상 클래스만 의존하도록 설계할 수 있습니다.
다음 포스팅에 자세한 설명을 이어가겠습니다.
객체지향의 핵심은 다형성
다형성 만으로는 클라이언트 코드 변경을 막을 수 없다.
다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함게 변경된다.
다형성 만으로는 OCP와 DIP를 지킬 수 없다.