publicabstractclassSuper{publicvoidtemplateMethod(){// 코드의 기본 로직을 담고 있는 "템플릿 메소드"// 기본 알고리즘 코드hookMethod();abstractMethod();...}protectedvoidhookMethod(){// 디폴트 기능이 정의되어 있거나 서브클래스에서 선택적으로 오버라이드 가능한 "훅 메소드"...}publicabstractvoidabstractMethod();// 서브클래스에서 반드시 구현해야하는 추상 메소드}publicclassSub1extendsSuper(){protectedvoidhookMethod(){...}publicvoidabstractMethod(){...}}
템플릿 메소드 패턴: 슈퍼클래스에 기본적인 로직의 흐름을 구현하고, 기능의 일부를 추상 메소드나 오버라이딩이 가능한 메소드로 만든 후, 서브클래스에서 필요에 맞게 구현하는 방법. 변하지 않는 기능은 슈퍼클래스에 구현하고 자주 변경되며 확장할 기능은 서브클래스에서 만들도록 한다.
팩토리 메소드 패턴: 서브 클래스에게 구체적인 오브젝트 생성 방법을 위임하는 방식. 슈퍼클래스 코드에서 서브클래스에서 구현하는 메소드를 호출하여 필요한 타입의 오브젝트를 얻는다.
디자인 패턴: 특정 상황에서 자주 발생하는 문제를 해결하기 위한, 재사용 가능한 솔루션. 패턴의 핵심이 담긴 목적 또는 의도를 잘 알고 적용할 상황 / 해결해야할 문제 / 각 요소의 역할을 잘 알아야 한다.
상속을 통한 확장의 한계점
다중 상속의 문제점
다른 목적으로 UserDao에 상속을 적용하기 힘듬
슈퍼클래스에 변경이 일어날 경우, 서브클래스도 함께 수정해야될 수도 있음
다른 DAO에 대해서 적용할 수가 없음
DAO의 확장
변화의 성격
모든 오브젝트가 다 동일한 방식으로 변하는 것이 아니다.
변화의 성격이 다르다는 것은 변화의 이유와 시기, 주기 등이 다르다.
변화의 성격이 다른 것을 분리하는 것이 “관심사의 분리”
클래스의 분리
DB 커넥션과 관련된 부분을 상속을 통한 서브클래스에서가 아닌, 별도의 클래스에서 구현 후 UserDao가 이를 사용하도록 함
publicabstractclassUserDao{privateConnectionMakerconnectionMaker;publicUserDao(){connectionMaker=newSimpleConnectionMaker();// 문제는 이 부분}publicvoidadd(Useruser)throwsClassNotFoundException,SQLException{Connectionc=connectionMaker.makeNewConnection();...}}
관계설정 책임의 분리
UserDao에는 아직 분리되지 않은, “관계설정” 에 대한 관심이 분리되어 있지 않다.
UserDao의 모든 코드는 ConnectionMaker 인터페이스 외에는 어떤 클래스와도 관계를 가져서 안되게 해야 한다.
UserDao를 사용하는 쪽에서 “관계설정” 에 대한 책임을 부여한다.
UserDao가 어떤 ConnectionMaker 인터페이스의 구현 객체를 사용할지 결정하여 다이나믹한 관계 가 설정되도록 한다.
외부에서 만든 오브젝트를 전달받기 위해, 메소드 파라미터나 생성자 파라미터를 사용
의존관계
오브젝트 사이에서 런타임시에 맺어지는 관계
런타임 오브젝트 관계를 생성해주는 것은 UserDao를 사용하는 “클라이언트”의 책임이다.
ConnectionMaker 인터페이스를 구현하기만 했다면, 다른 DAO에 대해서도 유연하게 적용할 수 있다.
DB 접속 방법에 대한 관심과 DB를 사용하는 관심을 서로 분리했기 때문이다.
원칙과 패턴
개방 폐쇄 원칙
클래스나 모듈은 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.
클래스나 모듈의 책임을 담당하는 코드를 변경하지 않으면서도 기능을 추가할 수 있도록 설계가 되어야 한다. OCP를 위반하지 않는 설계를 할 때 중요한 것은 무엇이 변하는지, 무엇이 변하지 않는 것인지를 구분해야 한다.
UserDao는 “DB 연결 방법” 이라는 기능에 대해서는 열려 있고, UserDao의 핵심 코드는 변화에 영향을 받지 않고 닫혀 있다.
UserDao에 전혀 영향을 주지 않고 기능은 확장할 수 있고, 기능 확장시 UserDao의 코드는 영향을 받지 않는다.
객체 지향 설계 원칙
- 객체지향의 특징을 잘 살릴 수 있는 설계의 특징
원칙: 예외는 있을 수는 있지만, 대부분의 상황에 잘 들어맞는 가이드라인. 좀 더 일반적인 상황에서 적용 가능한 설계 기준이라 할 수 있다.
SOLID
* SRP(The Single Responsibility Principle): 단일 책임 원칙
* OCP(The Open Closed Principle): 개방 폐쇄 원칙
* LSP(The Liskov Substitution Principle): 리스코프 치환 원칙
* ISP(The Interface Segregation Principle): 인터페이스 분리 원칙
* DIP(The Dependency Inversion Principle): 의존관계 역전 원칙
높은 응집도와 낮은 결합도
높은 응집도
모듈, 클래스가 한 책임이나 관심사만 다루고 있다.
오브젝트의 책임이 아닌, 연관이 없는 외부 관심이나 책임이 얽혀 있지 않다.
불필요하거나 직접 관련이 없는 외부의 관심이나 책임이 얽혀 있지 않다.
어떤 책임이나 관심사에 대한 요구사항이 변화하면, 해당 모듈의 구성요소들이 함께 바뀐다.
응집도: 한 프로그램 요소가 얼마나 뭉쳐 있는가를 나타내는 척도
한 모듈, 클래스가 책임을 많이 질수록, 내부에서 서로 다른 역할을 수행하는 코드끼리 강하게 결합되며 이는 요구사항 변화에 유연하게 대처하기 힘들다. (응집도가 낮다)
낮은 결합도
책임과 관심사가 다른 오브젝트들간의 관계는 느슨하게 연결된 형태를 유지하며, 서로의 변경에 대해 영향을 거의 받지 않는다.
느슨한 연결: 관계를 유지하는데 꼭 필요한, 최소한의 간접적인 형태로 제공
결합도가 낮으면 변화에 쉽게 대응할 수 있고 확장에도 편리하다.
결합도: 프로그램 구성 요소들 사이가 얼마나 의존적인지를 나타내는 척도
한 오브젝트에서 변경이 일어나면 이와 연관된 다른 오브젝트에게 변화를 요구하는 정도. 당연히 결합도가 낮을수록 변화에 잘 대응하는 코드이다.
전략 패턴
자신의 컨텍스트에서 필요에 따라 변경이 필요한 알고리즘 / 루틴을 인터페이스를 통해 외부로 분리하고, 이 인터페이스를 통한 느슨한 관계를 이용하여 필요에 따라 바꿔서 사용하는 디자인 패턴이다.
UserDao의 경우 DB 연결 이라는 기능을 필요에 따라 인터페이스를 통해 바꾸면서 사용할 수 있다.
스프링은 이러한 객체지향적 설계 원칙과 디자인 패턴에 나타난 장점을 개발자들이 활용할 수 있도록 해주는 프레임워크이다.
제어의 역전 (IoC)
오브젝트 팩토리
팩토리, factory: 객체 생성 방법을 결정하고 만들어진 오브젝트를 리턴
오브젝트 생성 및 런타임 의존관계 설정을 팩토리로 위임
UserDao 및 ConnectionMaker는 각기 관심사항에 대한 비즈니스 로직을 담당
팩토리는 애플리케이션을 구성하는 컴포넌트들의 구조와 관계를 정의한 설계도 같은 역할을 수행한다.
애플리케이션의 컴포넌트 역할 을 하는 오브젝트와 애플리케이션의 구조를 결정 하는 오브젝트를 분리하느데 의미가 있다.
제어의 역전: 프로그램의 제어 흐름 구조가 뒤바뀜
일반적인 프로그램: 프로그램이 시작되는 지점에서 사용할 오브젝트를 직접 결정, 생성, 메소드 호출하고 결정한다.
제어의 역전은 이러한 흐름을 뒤집은 것이다.
사용할 오브젝트를 스스로 선택하지 않으며, 생성하지도 않는다. 모든 제어 권한을 다른 대상에게 위임한다.
관심사를 분리하고 책임을 나누고, 유연하게 확장 가능한 구조를 만들면서 자연스럽게 IoC가 적용된다.
프레임워크도 제어의 역전 개념이 적용된 기술이다. 프레임워크는 단지 만들어져있거나 확장해서 사용될 수 있도록 준비된 집합이 아니다. 애플리케이션 코드가 프레임워크에 의해 사용되는, 제어의 역전의 개념이 들어가 있다. 이와 달리 라이브러리의 경우 애플리케이션 코드가 직접 흐름을 제어한다.
DaoFactory 가 컴포넌트 생성 및 관리의 책임을 지고, 다른 컴포넌트들은 수동적으로 동작하므로 제어의 역전이 적용된 것이다.
제어의 역전에서는 프레임워크 또는 컨테이너와 같이 애플리케이션 컴포넌트의 생성과 관계 설정 등을 관리할 존재가 필요하다.
스프링의 IoC
애플리케이션 컨텍스트와 설정정보
빈 (Bean): 스프링이 제어권을 가지고, 직접 생성하고 관계를 부여하는 오브젝트
스프링 빈: 스프링 컨테이너가 생성과 관계설정, 사용 등을 제어해주는 제어의 역전이 적용된 오브젝트
빈 팩토리, 애플리케이션 컨텍스트: 스프링에서 빈의 생성과 제어를 담당하는 IoC 오브젝트
애플리케이션 컨텍스트는 별도의 정보를 참고하여 빈의 생성 및 관계 설정과 같은 제어 작업을 총괄
packagech01.springbook.user.dao;@ConfigurationpublicclassDaoFactory{@BeanpublicUserDaouserDao(){returnnewUserDao(connectionMaker());}// 메소드의 이름이 빈의 이름이 된다.@BeanpublicConnectionMakerconnectionMaker(){returnnewSimpleConnectionMaker();}}...publicclassUserDaoTest{publicstaticvoidmain(String[]args)throwsClassNotFoundException,SQLException{ApplicationContextapplicationContext=newAnnotationConfigApplicationContext(DaoFactory.class);UserDaodao=applicationContext.getBean("userDao",UserDao.class);...}
AnnotationConfigApplicationContext: @Configuration annotation을 사용하는, 자바 코드를 설정 정보를 사용할 때 이 애플리케이션 컨택스트를 사용
getBean: 애플리케이션 컨텍스트가 관리하는 오브젝트를 요청하는 메소드
애플리케이션 컨텍스트의 동작방식
오브젝트 팩토리에 대응되는 것이 스프링의 애플리케이션 컨텍스트
스프링에서는 IoC 컨테이너라 하기도 하고, 스프링 컨테이너라고 부르기도 한다.
애플리케이션 컨텍스트는 ApplicationContext 인터페이스를 구현하며, 빈 팩토리가 구현하는 BeanFactory 인터페이스를 상속한다.
애플리케이션에서 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당
@Configuration 이 붙은 DaoFactory를 IoC 설정정보로 활용하며, DaoFactory의 userDao 메소드를 호출해서 오브젝트를 가져온 것을 getBean 을 통해 요청될 때 전달해준다.
@Bean 이 붙은 메소드의 이름을 가져와 빈 목록을 만들고, 요청시 해당 메소드를 호출하여 오브젝트를 생성 후 전달
애플리케이션 컨택스트를 사용하는 이유는 범용적이고 유연한 방법으로 IoC 기능을 확장하기 위해서이다.
클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.
어떤 팩토리 클래스를 사용할지 알 필요가 없으며, 팩토리 오브젝트를 생성할 필요도 없다.
XML 을 통한 단순한 방법을 통해 설정 정보를 만들 수 있다.
종합 IoC 서비스 제공
오브젝트가 만들어지는 방식, 시점과 전략을 유연하게 가져갈 수 있다.
빈을 검색하는 다양한 방법을 제공
이름 뿐만 아니라, 타입이나 특별한 annotation 설정이 되어 있는 빈을 찾을 수도 있다.
스프링 IoC의 용어 정리
빈 (Bean): 스프링이 IoC 방식으로 관리하는 오브젝트, 스프링이 직접 그 생성과 제어를 담당하는 오브젝트
빈 팩토리 (Bean Factory): 스프링의 IoC 를 담당하는 핵심 컨테이너, 빈을 등록 / 생성 / 조회 / 전달하며 관리하는 기능을 담당
애플리케이션 컨텍스트 (Application Context): 빈 팩토리를 확장한 IoC 컨테이너. 스프링이 제공하는 각종 부가 서비스를 추가로 제공
설정정보/설정 메타정보 (Configuration Metadata): IoC를 적용하기 위해 사용하는 메타정보. IoC 컨테이너에 의해 관리되는 애플리케이션 오브젝트를 생성하고 구성할 때 사용.
컨테이너, IoC 컨테이너: IoC 방식으로 빈을 관리한다는 의미로 애플리케이션 컨텍스트나 빈 팩토리를 컨테이너 또는 IoC 컨테이너라고도 부른다.
스프링 프레임워크: IoC 컨테이너, 애플리케이션 컨텍스트를 포함해서 스프링이 제공하는 모든 기능을 총칭
싱글톤 레지스트리와 오브젝트 스코프
애플리케이션 컨텍스트를 통해 생성된 빈은 디폴트로 몇번 요청하더라도 동일한 빈이 전달된다.
매번 동일한 빈을 돌려준다. (매번 new 메소드를 통해 새로운 빈이 생성되지 않는다.)
애플리케이션 컨텍스트는 빈을 싱글톤 으로서 저장하고 관리하는 싱글톤 레지스트리 이다.
스프링이 주로 적용되는 대상이 서버 환경인데, 이 환경은 수많은 요청을 처리해야 하는 높은 성능이 요구된다. 각 요청에 대해 새로운 오브젝트를 생성한다면 부하가 걸리게 되므로, 하나의 오브젝트만 만들어두고 요청을 처리하는 스레드들이 서로 오브젝트를 공유하면서 사용하도록 한다.
싱글톤 패턴: 어떤 클래스를 애플리케이션에서 제한된 인스턴스 개수, 주로 하나만 존재하도록 강제하는 패턴.
<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"><beanid="connectionMaker"class="ch01.springbook.user.dao.SimpleConnectionMaker"/><!-- bean 태그는 @Bean에 대응 --><!-- class 에는 오브젝트 생성시 사용하는 클래스 이름, 패키지까지 모두 포함 --><beanid="userDao"class="ch01.springbook.user.dao.UserDao"><propertyname="connectionMaker"ref="connectionMaker"/><!-- Setter 를 통한 의존관계 주입, UserDao 클래스에는 setConnectionMaker 이름의 setter가 있어야 한다. --><!-- name은 Setter의 프로퍼티 이름 --><!-- ref는 Setter 를 통해 주입할 오브젝트의 빈 ID이다. --></bean></beans>
XML 을 통한 설정정보를 활용하기 위해서는 GenericXmlApplicationContext 를 사용한다.