객체지향에 대해서 이야기하는데, 스프링의 핵심에 대해서 이야기 하는 이유는 무엇일까? 우리가 생각하는 스프링의 핵심은 무엇일까? 단지 웹 애플리케이션을 만들고, DB 접근을 해서 데이터를 전달해주는 것일까? 스프링의 진짜 핵심은 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크이다. 좋은 객체 지향 설계를 위해서는 객체 지향 설계에 대해서 잘 알아야 한다.
객체 지향의 특징으로는 4가지가 있다.
다형성에 대해서 위에서 '객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있는 성질'이라고 하였다. 아래 그림을 보면 더 이해가 될 것 이다.
자동차 서비스는 자동차 인터페이스를 바라보고 있다. 그리고 자동차 인터페이스는 내연차와 전기차로 구현되어 있다.
자동차 서비스에서 연료주입을 하는 경우 자동차 인터페이스만 보면 된다. 즉 내연차나 전기차의 연료주입 메서드를 모두 사용할 수 있다는 것이다.
다형성을 이용한다면 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다.
인터페이스를 통해서 역할과 구현을 명확하게 분리할 수 있다. 이렇게 된다면 확장이 가능한 설계를 할 수 있다. 위에 정의한 자동차 인터페이스에 구현 클래스 추가가 용이하다. 뭐 예를 들자면 수소차가 있지 않을까 싶다. 즉 역할은 자동차이고, 구현은 인터페이스를 구현한 클래스가 될 것이다. 이렇게 역할과 구현이 명확하게 분리가 된다면 확장에 대해서 용이하다.
만약에 역할(인터페이스)자체가 변경이 된다면 많은 변경 비용이 발생한다. 자동차가 비행기로 변경이 된다면, 구현 클래스들은 모두 변경이 된다. 그러므로 역할(인터페이스)를 안정적으로 잘 설계하는 것이 중요하다.
하나의 클래스는 하나의 책임만 가져야 한다. 하나의 클래스는 명확한 기준을 가져야 한다는 뜻이다. 자동차에 대한 클래스를 만든다면 자동차를 변수와 메서드를 가져야 하는 것이다. 기준에 대해서 더 생각을 해본다면 클래스에 대해서 변경이 있을 때, 변경점이 많이 발생하지 않는 다면 원칙을 잘 따른 것이다.
소프트웨어 요소에 확장은 열려 있으나 변경에는 닫혀 있어야 한다. 이때 다형성을 많이 사용한다. 인터페이스에 새로운 구현 클래스를 생성한다면, 확장에 열려 있고 변경에 닫혀 있는 것을 볼 수 있다. (ex 자동차 인터페이스에 수소차 구현 클래스를 추가하는 경우)
서브 타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다. 즉 부모 객체를 호출하는 동작에서 자식 객체가 부모 객체를 완전히 대체할 수 있다는 원칙이다. 객체 지향 프로그래밍에서 상속이 일어나면, 하위 타입은 상위 타입의 특성을 가질 수 있으며, 그 특성을 토대로 확장할 수 있다. 올바른 상속을 위한 원칙이다.
클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안된다. 즉 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다는 뜻이다. 예를 들어서 자동차 인터페이스를 생각 해보면, 자동차에 대해서 생각해보면 여러개가 있지만 대표적으로 운전하는 것, 정비하는 것이 있다. 자동차 인터페이스 자체를 운전/정비 인터페이스로 분리해서 관리하는 것이 더 명확해지고, 대체하기도 쉽다. 범용적인 인터페이스를 여러개로 분리해서 관리하자는 원칙이다.
고차원 모듈은 저차원 모듈에 의존하면 안된다. 즉 추상화에 의존해야지, 구체화에 의존하면 안된다는 뜻이고, 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻이다. 인터페이스에 의존해야 유연하게 구현 클래스를 변경할 수 있다는 원칙이다.
스프링 프레임워크를 올바르게 사용하기 위해서 객체 지향에 대해서 공부를 해보았다.
참고자료