[토비의 스프링] 1.1 ~ 1.3

개발새발log·2022년 8월 5일
0

1.1. 초난감 DAO

  • User
public class User {
    String id;
    String name;
    String password;

    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;
    }
}
  • UserDao
public class UserDao {
    public void add(User user) throws ClassNotFoundException, SQLException {
        Connection c = DriverManager.getConnection(
                "jdbc:h2:~/tobyspring", "sa", "");

        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();
    }

    public User get(String id) throws ClassNotFoundException, SQLException {
        Connection c = DriverManager.getConnection(
                "jdbc:h2:~/tobyspring", "sa", "");

        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;
    }
}
  • DAO 태스트용 main() 메소드
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
    
		UserDao dao = new UserDao();

		User user = new User();
		user.setId("yylee");
		user.setName("유영");
		user.setPassword("1234");

		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() + " 조회 성공");

	}

콘솔 결과:

> yylee 등록 성공
> 유영
> 1234
> yylee 조회 성공

1.2. DAO의 분리

관심사의 분리

  • 변경을 최소화 -> 분리와 확장을 고려한 설계가 중요하다
  • 문제는 변화는 대체로 한 가지 관심에 대해 일어나지만 그에 따른 작업은 한 곳에 집중되지 않는 경우가 많다는 것이다
  • 관심이 같은 것끼리는 모으고, 관심이 다른 것은 따로 떨어져 있게 하자!
    = 관심사의 분리, Separation of Concerns

커넥션 만들기 추출 - 리팩토링

  • UserDao의 관심사항:
    a) DB 연결
    b) SQL 실행
    c) 사용한 리소스 반환
  1. 중복코드의 메소드를 추출하기
private Connection getConnection() throws SQLException {
      return DriverManager.getConnection("jdbc:h2:~/tobyspring", "sa", "");
}
  1. DB 커넥션 만들기 독립
  • N사와 D사가 각기 다른 종류의 DB를 사용하며 독자적인 방식을 적용하고 싶다면?
  • 상속을 통해 서브클래스로 확장
    - 어떻게 데이터를 등록하고 가져올 것인가 -> UserDao
    - DB연결 방법을 어떻게 할 것인가 -> NUserDao, DUserDao

등장하는 개념 (상속과 관련)

✅ 템플릿 메소드 패턴
✅ 팩토리 메소드 패턴

템플릿 메소드 패턴

슈퍼클래스에서 기본적인 로직의 흐름을 추상 메소드/protected 메소드로 템플릿처럼 제공하고, 서브클래스에서 이를 구체적으로 구현하는 방식

팩토리 메소드 패턴

구체적으로 어떤 인스턴스를 만들지는 서브 클래스에서 결정하는 방식

👉 UserDao의 getConnection은 템플릿 메소드이자 팩토리 메소드라고 볼 수 있음

단점

  1. 상속 -> 후에 다른 목적으로 상속 적용하기 힘들 수 있다
  2. 상속을 통한 상하위 클래스의 관계는 생각보다 밀접하다
  3. 커넥션 만드는 코드를 다른 DAO에 적용하기 힘듦, 중복 발생

1.3 DAO의 확장

클래스의 분리

public class UserDao {

    private SimpleConnectionMaker simpleConnectionMaker;

    public UserDao(){
        simpleConnectionMaker = new SimpleConnectionMaker();
    }
    
    public void add(User user) throws SQLException {
        Connection c = simpleConnectionMaker.makeNewConnection();
    	//...
	}
    
    public User get(String id) throws SQLException {
        Connection c = simpleConnectionMaker.makeNewConnection();
        //...
    }
}
  • 별개의 DB 연결 클래스
public class SimpleConnectionMaker {

    public Connection makeNewConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:h2:~/tobyspring", "sa", "");
    }
}

문제

  • DB 커넥션을 가져오는 방법은 자유로운 확장하기 어려움
  • 원인은 UserDao가 DB 커넥션을 가져오는 구체적인 방법에 종속되기 때문
    (UserDao -> SimpleConnectionMaker)

인터페이스의 도입

  • 확실히 분리하되, 결합도를 느슨하게 하려면?
  • 추상화하기 (공통적인 성격을 뽑아내 따로 분리해내는 작업)
    👉 인터페이스
public interface ConnectionMaker {
    public Connection makeConnection() throws SQLException;
}
  • UserDao
public class UserDao {
    private ConnectionMaker connectionMaker;

    public UserDao(){
        connectionMaker = new NConnectionMaker(); //구체 클래스에 의존!
    }
    
    //...
  • UserDao와 UserDao가 사용할 ConnectionMaker 구현 클래스 사이의 관계를 설정해주는 것에 대한 관심사 포함

관계설정 책임의 분리

  • 외부에서 메소드 파라미터나 생성자 파라미터로 오브젝트 전달받도록 수정
public UserDao(ConnectionMaker connectionMaker){
    this.connectionMaker = connectionMaker;
}
  • 클라이언트의 책임: 런타임 오브젝트 관계를 갖는 구조로 만들어주는 것

👉 UserDao -> 클라이언트로 관심사항, 책임을 떠넘김

  • 클라이언트 코드
ConnectionMaker connectionMaker = new DConnectionMaker();
UserDao dao = new UserDao(connectionMaker); //UserDao-ConnectionMaker 의존관계 설정

결과적으로,

  • 개방 폐쇄 원칙을 잘 따르고 있으며
  • 응집력이 높고 결합도는 낮으며
  • 전략 패턴을 적용
profile
⚠️ 주인장의 머릿속을 닮아 두서 없음 주의 ⚠️

0개의 댓글