Lecture-01-1

Download Report

Transcript Lecture-01-1

Chapter 1.
오브젝트와 의존관계
Sep. 2014
Youn-Hee Han
http://link.koreatech.ac.kr
1.1 초난감 DAO
2
1.1.1 User
 User 도메인 객체
– User.java
• 자바빈(Javabeans)
 디폴트 생성자
 프로퍼티
package book30.ch01.domain;
public class User {
String id;
String name;
String password;
» setter
» getter
– DB 테이블
DROP TABLE IF EXISTS `users`;
CREATE TABLE users (
id
varchar(10) primary key,
name
varchar(20) not null,
password varchar(10) not null
)
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
2
1.1.1 User
 자바빈
– 다음 두 가지 관례에 따라 만들어진 객체
• 1) 디폴트 생성자
 자바빈은 파라미터가 없는 디폴트 생성자를 갖고 있어야 한다.
 프레임워크에서 리플렉션(Reflection)을 이용하여 객체를 생성할 때 디
폴트 생성자가 활용된다
• 2) 프로퍼티
 자바빈이 노출하는 이름을 가진 속성
 프로퍼티는 set으로 시작하는 수정자 메소드 (setter)와 get으로 시작하
는 접근자 메소드 (getter)를 이용해 수정 또는 조회할 수 있다.
2
1.1.2 UserDao
 UserDao
– DAO (Data Access Object)
• DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하는 객체
public class UserDao {
public void add(User user) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection c
= DriverManager.getConnection("jdbc:mysql://localhost/springbook?characterEncoding=UTF-8",
“spring",
"book");
PreparedStatement ps = c.prepareStatement("insert into users(id, name, password) values(?,?,?)");
ps.setString(1, user.getId());
ps.setString(2, user.getName());
ps.setString(3, user.getPassword());
ps.executeUpdate();
}
ps.close();
c.close();
5
1.1.1 UserDao
 UserDao
– DAO (Data Access Object)
public User get(String id) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection c
= DriverManager.getConnection("jdbc:mysql://localhost/springbook?characterEncoding=UTF-8",
“spring", "book");
PreparedStatement ps = c.prepareStatement("select * from users where id = ?");
ps.setString(1, id);
ResultSet rs = ps.executeQuery();
rs.next();
User user = new User();
user.setId(rs.getString("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
rs.close();
ps.close();
c.close();
}
}
return user;
6
1.1.3 main()을 이용한 테스트 코드
테스트 코드
public static void main(String[] args) throws ClassNotFoundException, SQLException {
UserDao dao = new UserDao();
User user = new User();
user.setId("whiteship");
user.setName("백기선");
user.setPassword("married");
dao.add(user);
System.out.println(user.getId() + " 등록 성공");
}
User user2 = dao.get(user.getId());
System.out.println(user2.getName());
System.out.println(user2.getPassword());
System.out.println(user2.getId() + " 조회 성공");
– 잘 동작하는 위 코드를 수정하고 개선해야 하는 이유?
– 스파게티 코드 (Spaghetti Code)
7
1.2 DAO의 분리
2
1.2.1 관심사의 분리
 관심사의 분리 (Separation of Concerns)
– 관심이 같은 사항들은 하나의 객체 또는 주변 객체로 모음
– 관심이 다른 사항들은 가능한 따로 떨어져서 서로 영향을 주지
않도록 분리
– 소프트웨어 공학의 모듈화 (Modulization)와 유사
 개발자에게 요구되는 역량
– 미래의 변화를 어떻게 대비하는가?
– 비지니스 로직의 변화는 집중된 한가지 관심에 대해 일어나지만
그에 따른 코딩 작업은 한 곳에 집중되지 않는 경우가 많다.
– 그러한 로직의 변화에 대처하여 자유롭고 편리하게 변경, 발전,
확장시킬 수 있는 역량 필요
9
1.2.2 커넥션 만들기의 추출
 UserDao에서의 관심사항 예
– DB 연결을 위한 커넥션 설정
– SQL 쿼리문 생성 및 실행
– 리소스 반환
 Refactoring (리펙토링)
– 외부의 동작방식에는 변화 없이 내부 구조를 변경해서 코드를
재구성하는 작업 또는 기술
– 코드 내부 설계가 개선되어 코드를 이해하기가 편해지고 이후
변화에 효율적으로 대응가능
– 생산성 및 코드 품질이 올라감
– DB 연결을 위한 커넥션 설정 리펙토링
• Extract Method (메소드 추출)
10
1.2.2 커넥션 만들기의 추출
 중복 코드를 단일 메소드로 추출 (Extract Method)
11
1.2.3 DB커넥션 만들기의 독립
 변화에 유연히 대처가 가능한 DAO
– 상황설명
• 본인이 만든 UserDao가 유명해져서 N사와 D사에서 구매 주문이 들
어옴.
• N사와 D사가 각기 다른 종류의 DB를 사용하고 있어서 DB 커넥션
을 가져오는 방법이 상이함.
• DB 커넥션을 가져오는 방법은 납품 이후에도 변경될 가능성이 있음.
• N사와 D사에 소스코드는 공개하지 않고 Binary 파일만을 제공하려
고 함.
• N사와 D사 스스로 원하는 DB 커넥션 생성 방식을 적용해가면서
UserDao를 사용하게 하려고 함.
12
1.2.3 DB커넥션 만들기의 독립
 변화에 유연히 대처가 가능한 DAO
– 상속 (Inheritance)를 통한 확장
13
1.2.3 DB커넥션 만들기의 독립
 변화에 유연히 대처가 가능한 DAO
– 상속 (Inheritance)를 통한 확장
14
1.2.3 DB커넥션 만들기의 독립
 변화에 유연히 대처가 가능한 DAO
– 상속 (Inheritance)를 통한 확장
• 새로운 DB 연결방법을 적용해야 할 때에는 UserDao를 상속하는 새
로운 클래스 작성하여 사용
15
1.2.3 DB커넥션 만들기의 독립
 템플릿 메소드 패턴 (Template Method Pattern)
– 슈퍼클래스에 변하지 않는 기본 로직을 구현하고 자주
변경되거나 확장이 이후에 요구될 수 있는 일부 기능을 추상
메소드나 오버라이딩이 가능한 protected 메소드로 구현
• 이러한 메소드는 hookMethod 라고도 불리운다.
– 서브클래스에서 hookMethod를 필요에 맞게 구현하여 사용
– 팩토리 메소드 패턴 (Factory Method Pattern)이라고도 함
16
1.2.3 DB커넥션 만들기의 독립
 팩토리 메소드 패턴 (Factory Method Pattern)
– 서브클래스에서 구체적인 객체 생성 방법을 결정하게 하는 코딩
방법
• getConnection()을 통해 만들어진 Connection 객체의 종류가 달라
질 수 있는 것을 목적으로 함
• 슈퍼클래스인 UserDao에서는 자신이 사용할 Connection 인터페이
스 타입의 객체라는 것 이외에는 관심을 두지 않는다.
17
1.2.3 DB커넥션 만들기의 독립
 디자인 패턴
– 개발자들 사이의 언어
• “UserDao에 팩토리 메소드 패턴을 적용해서 getConnection()을
분리합시다.”
18
1.2.3 DB커넥션 만들기의 독립
 상속 (Inheritance)를 통한 확장의 단점
– 다중 상속 문제
• NUserDao가 UserDao외에 다른 클래스도 상속을 받아야 할 때 다
중 상속 허용 안됨
– 슈퍼클래스와 서브클래스의 긴밀한 결합 문제
• 슈퍼 클래스에 변경이 생기면 서브 클래스도 함께 수정해야 할 필요
가 종종 있음
– 생성한 서브클래스는 다른 클래스에는 적용될 수 없음.
• 만약 UserDao 외에 DAO 클래스가 계속 만들어진다면 거의 동일한
구현코드를 가지는 새로운 서브클래스를 함께 계속 만들어 주어야
함
19
1.3 DAO의 확장
2
1.3.1 클래스의 분리
 두 개의 별다른 관심 사항
데이터 엑세스 로직을 어떻게
만들 것인가?
SQL 쿼리 문 구성?
DB에 저장할 정보?
DB에서 꺼내올 정보?
DB연결을 어떻게 만들 것인가?
어떤 드라이버를
사용할 것인가?
연결 URL?
연결시 사용할 계정 정보?
21
1.3.1 클래스의 분리
 클래스의 완전 분리
– 두 개의 독립된 클래스로 분리
22
1.3.1 클래스의 분리
 클래스의 완전 분리
– 두 개의 독립된 클래스로 분리
makeNewConnection() throws ClassNotFoundException, SQLException {
23
1.3.1 클래스의 분리
 클래스의 완전 분리
– 두 개의 독립된 클래스로 분리
• 앞 코드의 문제점
 UserDao 클래스 코드가 SimpleConnectionMaker라는 특정 클래스에
종속
 DB 커넥션 생성 방법을 변경한 새로운 클래스를 사용하기 위해서는
UserDao의 소스도 함께 변경해야 함.
 결국 N사와 D사에 UserDao의 소스코드도 함께 제공해야 함.
• 근본적인 문제의 원인
 UserDao에서 DB 커넥션을 가져오기 위한 정보 (클래스 이름, 메소드
이름)를 너무 많이 알고 있기 때문임
simpleConnectionMaker = new SimpleConnectionMaker();
…
Connection c = simpleConnectionMaker.makeNewConnection();
 따라서 UserDao가 DB 커넥션을 제공하는 클래스에 종속됨
24
1.3.2 인터페이스의 도입
 인터페이스의 역할
– 두 개의 클래스가 서로 긴밀하게 종속되지 않도록 중간에 추상
적인 느슨한 연결고리를 만들어 줄 수 있음
 추상화 (Abstraction)
– 다수의 것들에 대해 공통적인 성격을 뽑아내어 따로 분리해 내
는 작업
25
1.3.2 인터페이스의 도입
 인터페이스를 도입한 코드
– 인터페이스 정의
– 인터페이스 구현 클래스
26
1.3.2 인터페이스의 도입
 인터페이스를 도입한 코드
– 인터페이스를 도입한 UserDao 개선
27
1.3.2 인터페이스의 도입
 인터페이스의 도입
– 여전히 존재하는 문제점
• UserDao에 어떠한 구현 클래스를 사용하는지 결정하는 생성자 호
출 코드가 여전히 남아 있음
connectionMaker = new DConnectionMaker();
클래스 사이에 불필요한 의존관계를 가지는 구조
28
1.3.3 관계설정 책임의 분리
 주목해야 할 관심(Concern) 사항
– UserDao에서 ConnectionMaker의 어떠한 특정 구현 클래스를
사용해야 하나?
 위 관심사를 UserDao에서 분리하는 것이 목표
– 그렇다면, 관계 설정 관심사를 담당할 클래스는?
• UserDao를 사용하는 클라이언트. 즉, Main 함수를 지니고 있는 클
래스
– UserDao는 이러한 관계 설정에 있어서 수동적이 됨
• UserDao가 클라이언트로 부터 관계 설정을 받아 내기 위한 방법 →
생성자 또는 메소드 활용
29
1.3.3 관계설정 책임의 분리
 관심사항의 철저한 분리
– UserDao에서 새롭게 구현된 생성자
– 관계설정 책임이 추가된 DserDAO 클라이언트인 UserDaoTest
클래스와 main() 메소드
30
1.3.3 관계설정 책임의 분리
 클라이언트인 UserDaoTest 클래스의 main() 메소드 역할
– UserDao에서 새롭게 구현된 생성자 UserDao와
ConnectionMaker 구현 클래스와의 런타인 (Runtime) 객체 의존
관계 설정
 이제부터 UserDao는 SQL을 생성하여 자료를 조회하고
변경하는 작업에만 집중할 수 있다.
– DB Connection 연결 방법이나 전략에는 전혀 고민 없음
31
1.3.3 관계설정 책임의 분리
 관계설정 책임을 담당한 UserDaoTest 클래스가 추가된
구조
– UserDao 생성자는 ConnectionMaker 인터페이스 타입으로 객체
를 전달받기 때문에 ConnectionMaker를 구현한 어떠한 클래스
의 객체라도 전달 받을 수 있다.
– 하지만, UserDao 객체는 전달받은 ConnectionMaker 객체가 어
떠한 객체인지 전혀 알지도, 알 필요도 없다.
32
1.3.4 원칙과 패턴
 개방 폐쇄 원칙 (OCP, Open-Closed Principle)
– 클래스나 모듈은 확장에는 열려있어야 하고 변경에는 닫혀있어
야 한다.
• UserDao에 영향을 주지 않고 인터페이스를 사용하여 DB 연결 방법
기능 확장에는 열려있다.
• 자신의 핵심 기능을 구현한 코드는 그러한 변화에 전혀 영향을 주지
않는다.
33
1.3.4 원칙과 패턴
 높은 응집도 (High Coherence)
– 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중되어
있어야 한다.
• ConnectionMaker는 자체의 응집력을 유지하면서 확장되고 발전할
수 있다.
• 이러한 발전이 UserDao등의 다른 클래스에 전혀 영향을 주지 않는
다.
 낮은 결합도 (Low Coupling)
– 책임과 관심사가 서로 다른 객체 또는 모듈끼리는 느슨하게 연
결된 형태 (서로간의 독립성)를 유지해야 한다.
– 결합도 (Coupling)
• 하나의 객체에 변경이 일어날 때 관계를 맺고 있는 다른 객체에 변화를 요구
하는 정도
– UserDao와 ConnectionMaker간의 관계는 매우 느슨하게 연결되
어 있다.
34
1.3.4 원칙과 패턴
 전략 패턴 (Strategy Pattern)
– 클래스를 구현할 때 필요에 따라 변경이 필요한 모듈은 인터페
이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인
클래스를 필요에 따라서 변경하여 (전략을 변경하여)
사용하는 디자인 패턴
– 어떠한 모듈 (UserDao)를 사용하는 클라이언트(UserDaoTest)는
그 모듈이 사용할 전략을 그 모듈의 생성자/메소드를 통해 제공
• 이 예제에서 전략은?
 ConnectionMaker를 구현한 클래스, 예를 들어 DConnectionMaker
35