본문 바로가기
Design Pattern

[Design Pattern] 팩토리 패턴

by 진꿈청 2024. 4. 4.

이번 포스팅으로 알아볼 디자인 패턴은 팩토리 패턴이다.

 

설명하기에 앞서 팩토리 패턴에 관하여 간단하게 설명하자면
공통된 종류를 갖는 객체들의 구현을 몰라도 팩토리 패턴을 통해 쉽게 생성하는 것을 말한다.

 

그렇다면 본격적으로 코드와 함께 알아보자.

 

DB 클래스들과 함께 팩토리 패턴에 접근하려 한다.

 

DIP(의존성 역전 원칙)에 의거하여 DB 인터페이스와 MariaDB, OracleDB를 생성해보자.

 

 

DB

public interface DB {

    public int execute(String sql);
    public void setUrl(String url);
}

 

MariaDB 

public class MariaDB implements DB {

    private String url;

    // 쿼리 실행 메서드
    public int execute(String sql){

        if(url == null){
            throw new NullPointerException("db driver not found exception");
        }

        if(sql.equals("select")){
            System.out.println("query execute : " +url+sql);
            return 1;
        } else{
            System.out.println("query fail : syntax error");
            return -1;
        }
    }

    // URL 세팅 메서드
    public void setUrl(String url){
        this.url = url;
    }

}

 

OracleDB

public class OracleDB implements DB{
    private String url;

    // 쿼리 실행 메서드
    public int execute(String sql){

        if(url == null){
            throw new NullPointerException("db driver not found exception");
        }

        if(sql.equals("select")){
            System.out.println("query execute : " +url+sql);
            return 1;
        } else{
            System.out.println("query fail : syntax error");
            return -1;
        }
    }

    // URL 세팅 메서드
    public void setUrl(String url){
        this.url = url;
    }
}

 

App

public class App {
    public static void main(String[] args) {
        DB mariaDB = new MariaDB();
	mariaDB.setUrl("jdbc:mariadb://");
        mariaDB.execute("select");

        DB oracleDB = new OracleDB();
        oracleDB.setUrl("jdbc:oracle:thin://");
	oracleDB.execute("select");
    }
}

 

이렇게 작성해도 되지만 몇 가지 아쉬운 점이 존재한다.

  1. 각 DB들의 사용에 필수로 필요한 메소드를 알야한다.
  2. 경우에 따라 메소드의 실행 순서가 중요할 수 있다.


이때, 사용되는 것이 팩토리 패턴이다.

 

팩토리 패턴을 위해 DBFactory 클래스를 생성해준다.

 

DBFactory

public class DBFactory {
    public DB createDB(String protocol){
        if(protocol.equals("maria")){
            DB mariaDB = new MariaDB();
            mariaDB.setUrl("jdbc:mariadb://");
            return mariaDB;
        } else if(protocol.equals("oracle")){
            DB oracleDB = new OracleDB();
            oracleDB.setUrl("jdbc:oracle:thin://");
            return oracleDB;
        } else{
          throw new NullPointerException("db driver not found exception");
        }
    }
}

 

이에 따라 App 클래스도 수정한다.

 

App

public class App {
    public static void main(String[] args) {
    	DBFactory dbFactory = new DBFactory();

        // 내가 MariaDB 객체를 알 필요가 없음
        // setURL 부터 해야 할지, execute 부터 해야 할지 생각할 필요가 없다.
        // 문자열만 추가해서 DB를 생성하면 됨. (내가 의존해야 할 것은 DBFactory)
        // DB를 추가하고 싶으면 DB 생성 후 dbFactory의 if문에 추가만 하면 됨.
        DB maria = dbFactory.createDB("maria");
        maria.execute("select");

        DB mysql = dbFactory.createDB("mysql");
        mysql.execute("select");


    }
}

 

우리는 DBFactory라는 팩토리 패턴을 위한 클래스를 만듦으로 다음과 같은 이점을 얻을 수 있다.(위의 DB 예제를 기준으로 작성)

 

  1. MariaDB 객체의 구성등을 알 필요가 없다.
  2. setURL 부터 해야 할지, execute 부터 해야 할지 생각할 필요가 없다.
  3. createDB에 문자열만 추가해서 DB를 생성하면 된다.(DBFactory 클래스만 의존)
  4. 새로운 DB를 추가하고 싶다면 새로운 DB 클래스 생성 후 dbFactory의 if문에 추가만 하면 된다.

 

그럼 직접 새로운 MySqlDB라는 것을 생성하여 직접 코드로 구현해보자.

동시에 팩토리 패턴에서 보통 팩토리 클래스는 싱글톤으로 만들어서 공통으로 사용한다.
해당 부분도 함께 구현하자.

 

MySqlDB

public class MySqlDB implements DB{
    private String url;

    // 쿼리 실행 메서드
    @Override
    public int execute(String sql){

        if(url == null){
            throw new NullPointerException("db driver not found exception");
        }

        if(sql.equals("select")){
            System.out.println("query execute : " +url+sql);
            return 1;
        } else{
            System.out.println("query fail : syntax error");
            return -1;
        }
    }

    // URL 세팅 메서드
    @Override
    public void setUrl(String url){
        this.url = url;
    }
}

 

DBFactory(싱글톤)

public class DBFactory {

    // 싱글톤 패턴
    private static DBFactory dbFactory = new DBFactory();

    private DBFactory(){

    }

    public static DBFactory getInstance(){
        return dbFactory;
    }

    // OCP 위배!!
    // OCP를 위배하면 프로그램을 잘못 만든 것이다. 이렇게 생각할 필요는 없다.
    // 패턴은 우리가 코드를 짤 때, 편하게, 협업, 클린한 코드를 짜게 해주는 것이기 때문에 예외는 있다.
    // 아래와 같은 경우는 추가해주는게 좋음
    // DB 회사가 매일 매일 변경된다던지, 새로운 Driver가 금방 금방 나온다던지, 그럴 수 없음. 몇년에 한번
    public DB createDB(String protocol){
        if(protocol.equals("maria")){
            DB mariaDB = new MariaDB();
            mariaDB.setUrl("jdbc:mariadb://");
            return mariaDB;
        } else if(protocol.equals("oracle")){
            DB oracleDB = new OracleDB();
            oracleDB.setUrl("jdbc:oracle:thin://");
            return oracleDB;
        } else if(protocol.equals("mysql")){
            DB mysqlDB = new MySqlDB();
            mysqlDB.setUrl("jdbc:mysqldb://");
            return mysqlDB;
        }else{
          throw new NullPointerException("db driver not found exception");
        }
    }
}

 

App

public class App {
    public static void main(String[] args) {
        // 싱글톤 제작
        DBFactory dbFactory = DBFactory.getInstance();

        // 내가 MariaDB 객체를 알 필요가 없음
        // setURL 부터 해야 할지, execute 부터 해야 할지 생각할 필요가 없다.
        // 문자열만 추가해서 DB를 생성하면 됨. (내가 의존해야 할 것은 DBFactory)
        // DB를 추가하고 싶으면 DB 생성 후 dbFactory의 if문에 추가만 하면 됨.
        DB maria = dbFactory.createDB("maria");
        maria.execute("select");

        DB mysql = dbFactory.createDB("mysql");
        mysql.execute("select");


    }
}

 

위 처럼 코드를 작성하면 우리는 dbFactory에 if문 하나만을 추가해서
MySqlDB의 객체 내부 구현을 모르고도 사용할 수 있게 되었다.

 

그러나, 이렇게 DBFactory 클래스에 if문을 추가하는 것은 OCP에 위반한다.

하지만, OCP 위반에 관하여 우리가 중요하게 알아야 할 것은 다음과 같다.

 

  • OCP를 위배하면 프로그램을 잘못 만든 것이다. 이렇게 생각할 필요는 없다.
  • 패턴은 우리가 코드를 짤 때, 편하게, 협업, 클린한 코드를 짜게 해주는 것이기 때문에 예외는 있다.
  • 아래와 같은 경우는 추가해주는게 좋음.
  • DB 회사가 매일 매일 변경된다던지, 새로운 Driver가 금방 금방 나온다던지, 그럴 수 없음. 몇년에 한번.

 

이러한 이유들로 OCP에 너무 연연하지 않아도 된다.

적절하게 사용하는 것이 좋다.

 

 

 

여기까지 팩토리 패턴에 관해 알아보았다.