3.3.1 전략 클래스의 추가 정보
add() 메소드에 전략 패턴 적용
- add()에서는 PreparedStatement를 만들 때 user라는 부가적인 정보가 필요
- user를 제공해주어야 함
- AddStatement의 생성자를 통해 제공받도록
UserDao.java
public class UserDao {
public void deleteAll() throws SQLException {
StatementStrategy st = new DeleteAllStatement();
jdbcContextWithStatementStrategy(st);
}
}
AddAllStatement.java
public class AddAllStatement implements StatementStrategy {
User user;
public AddAllStatement(User user) {
this.user = user;
}
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
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());
return ps;
}
}
DAO가 깔끔해졌다.
3.3.2 전략과 클라이언트의 동거
코드의 문제점
- DAO 메소드마다 새로운 StatementStrategy 구현 클래스를 만들어야 함
- User와 같은 부가적인 정보가 있는 경우, 이를 위해 생성자와 저장해 둘 인스턴스 변수를 만들어야 함
로컬 클래스
StatementStrategy 전략 클래스를 UserDao 내부의 클래스로 정의
- DeleteAllStatement와 AddStatement는 UserDao에서 밖에 사용하지 않으며, UserDao 메소드 로직에 강하게 결합되어 있으므로 내부 클래스로 정의해도 큰 문제가 없다.
- UserDao가 아닌 UserDao의 메소드 내부에 클래스를 생성한다.
public class UserDao {
// ...
public void add(User user) throws SQLException {
class AddAllStatement implements StatementStrategy {
User user;
public AddAllStatement(User user) {
this.user = user;
}
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
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());
return ps;
}
}
StatementStrategy st = new AddAllStatement(user);
jdbcContextWithStatementStrategy(st);
}
public void deleteAll() throws SQLException {
class DeleteAllStatement implements StatementStrategy {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
PreparedStatement ps = c.prepareStatement("DELETE FROM users WHERE 1=1");
return ps;
}
}
StatementStrategy st = new DeleteAllStatement();
jdbcContextWithStatementStrategy(st);
}
}
중첩 클래스
다른 클래스 내부에 정의되는 클래스
중첩 클래스의 종류
- 스태틱 클래스
- 내부 클래스
- 멤버 내부 클래스(member inner class) : 오브젝트 레벨에 정의
- 로컬 클래스(local class) : 메소드 레벨에 정의
- 익명 내부 클래스(anonymous inner class) : 선언된 위치에 따라 다름
로컬 클래스의 장점
- add() 메소드 내부에서 PreparedStatement 생성 로직을 함께 볼 수 있음
- 클래스가 내부 클래스이므로 자신이 선언된 곳의 정보에 접근이 가능
- add() 메소드의 user 변수를 AddStatement에서 직접 사용 가능
- 단, 외부 변수는 반드시 final로 선언하여야 함
public class UserDao {
// ...
public void add(final User user) throws SQLException {
class AddAllStatement implements StatementStrategy {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
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());
return ps;
}
}
StatementStrategy st = new AddAllStatement();
jdbcContextWithStatementStrategy(st);
}
// ...
}
익명 내부 클래스
좀 더 간결하게 만들어보자
- AddStatement 클래스는 add() 메소드에서만 사용
- 클래스 이름도 제거할 수 있다.
익명 내부 클래스
- 이름을 갖지 않는 클래스
- 클래스 선언과 오브젝트 생성이 결합된 형태
- 상속할 클래스나 구현할 인터페이스를 생성자 대신 사용한 형태
- 클래스 재사용 필요가 없고, 구현한 인터페이스 타입으로만 사용할 경우에 유용
public class UserDao {
public void add(final User user) throws SQLException {
StatementStrategy st = new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
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());
return ps;
}
};
jdbcContextWithStatementStrategy(st);
}
}
익명 내부 클래스의 오브젝트는 한 번만 사용하면 되기 때문에 변수에 담아두지 말고 jdbcContextWithStatementStrategy() 메소드의 파라미터에서 바로 생성해도 무관 (deleteAll()도 동일)
public class UserDao {
// ...
public void add(final User user) throws SQLException {
jdbcContextWithStatementStrategy(
new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
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());
return ps;
}
}
);
}
public void deleteAll() throws SQLException {
jdbcContextWithStatementStrategy(
new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
PreparedStatement ps = c.prepareStatement("DELETE FROM users WHERE 1=1");
return ps;
}
}
);
}
// ...
}
' Spring > 토비의 스프링 3.1' 카테고리의 다른 글
3.5 템플릿과 콜백 (0) | 2019.01.23 |
---|---|
3.4 컨텍스트와 DI (0) | 2019.01.22 |
3.2 변하는 것과 변하지 않는 것 (0) | 2019.01.18 |
3.1 다시보는 초난감 DAO (0) | 2019.01.17 |
3.0 템플릿 (0) | 2019.01.17 |