팩터리 메서드 패턴

hodu·2024년 8월 25일
0

클린아키텍쳐

목록 보기
3/5
post-custom-banner

팩터리 메서드 패턴이란?

팩터리 메서드 패턴은 객체 생성을 직접 하는 대신, 객체 생성을 처리하는 메서드를 통해 객체를 만드는 디자인 패턴입니다. 이 패턴은 객체 생성 로직을 캡슐화하여 코드의 유연성과 재사용성을 높입니다.

데이터베이스 연결을 예시로 들어보겠습니다.

1. 문제 상황

여러 종류의 데이터베이스(MySQL, PostgreSQL 등)에 연결해야 하는 애플리케이션이 있습니다.
각 데이터베이스는 연결 방식이 조금씩 다르고 사용하는 드라이버가 다르며 쿼리 문법에도 일부 차이가 있을 수 있습니다.

우선 팩터리 메서드 패턴을 적용하지 않았을 때의 코드입니다.

public interface DatabaseConnection {
    void connect();
    void executeQuery(String query);
    void close();
}

class MySQLConnection implements DatabaseConnection {
    // Implementation
}

class PostgreSQLConnection implements DatabaseConnection {
    // Implementation
}

public class DatabaseClient {
    private DatabaseConnection connection;

    public DatabaseClient(String databaseType) {
        if ("MySQL".equals(databaseType)) {
            this.connection = new MySQLConnection();
        } else if ("PostgreSQL".equals(databaseType)) {
            this.connection = new PostgreSQLConnection();
        } else {
            throw new IllegalArgumentException("Unsupported database type: " + databaseType);
        }
        this.connection.connect();
    }

    public void executeQuery(String query) {
        try {
            this.connection.executeQuery(query);
        } catch (Exception e) {
            // 예외 처리
            System.err.println("Query execution failed: " + e.getMessage());
        }
    }

    public void close() {
        if (this.connection != null) {
            this.connection.close();
        }
    }
}

연결해야 하는 데이터베이스가 추가될 때마다 코드는 수정되어야 합니다. 이 코드는 OCP(Open-Closed Principle)를 위반하였습니다.
또한 DatabaseClient가 구체적인 데이터베이스 구현에 직접 의존하고 있습니다.

    private DatabaseConnection connection;
	...
    
    	this.connection = new MySQLConnection();
    	this.connection = new PostgreSQLConnection();

DatabaseClient 클래스는 구체적인 클래스인 MySQLConnection과 PostgreSQLConnection을 직접 인스턴스화하고 있다고 하는데요, 이는 다음과 같은 문제를 야기합니다.

  • DatabaseClient가 특정 데이터베이스 구현의 존재를 알아야 합니다.
  • 새로운 데이터베이스 타입을 추가하려면 DatabaseClient 클래스를 수정해야 합니다.
  • 구체적인 클래스에 의존하므로 다른 구현으로 쉽게 교체할 수 없습니다.

2. 팩터리 메서드 패턴 적용해보기

그럼 팩터리 메서드 패턴을 이용하여 위와 같은 문제점들을 수정해보겠습니다.
먼저, 데이터베이스 연결을 위한 인터페이스를 정의합니다.

public interface DatabaseConnection {
    void connect();
    void executeQuery(String query);
    void close();
}

이제 데이터베이스 연결들을 DatabaseConnection 인터페이스를 이용하여 구현합니다.

public class MySQLConnection implements DatabaseConnection {
    @Override
    public void connect() {...}

    @Override
    public void executeQuery(String query) {...}

    @Override
    public void close() {...}
}

public class PostgreSQLConnection implements DatabaseConnection {
    @Override
    public void connect() {...}

    @Override
    public void executeQuery(String query) {...}

    @Override
    public void close() {...}
}

이제 데이터베이스 연결을 생성하는 팩터리 인터페이스를 정의합니다.

public interface DatabaseConnectionFactory {
    DatabaseConnection createConnection();
}

그리고 각 데이터베이스 유형에 대한 구체적인 팩터리를 구현합니다. 팩터리 메서드 패턴의 특징인 생성한 객체도 반환해줍니다.

public class MySQLConnectionFactory implements DatabaseConnectionFactory {
    @Override
    public DatabaseConnection createConnection() {
        return new MySQLConnection();
    }
}

public class PostgreSQLConnectionFactory implements DatabaseConnectionFactory {
    @Override
    public DatabaseConnection createConnection() {
        return new PostgreSQLConnection();
    }
}

이제 이 팩터리를 사용하는 클라이언트 코드를 작성해보겠습니다.
이 코드에서는 데이터베이스 관련 로직을 처리합니다.

public class DatabaseClient {
    private DatabaseConnectionFactory factory;

    public DatabaseClient(DatabaseConnectionFactory factory) {
        this.factory = factory;
    }

    public void executeQuery(String query) {
        DatabaseConnection connection = factory.createConnection();
        connection.connect();
        connection.executeQuery(query);
        connection.close();
    }
}

아래 코드는 Main 클래스에서 실제로 사용하는 예시입니다.

public class Main {
    public static void main(String[] args) {
        // MySQL 사용
        DatabaseConnectionFactory mysqlFactory = new MySQLConnectionFactory();
        DatabaseClient mysqlClient = new DatabaseClient(mysqlFactory);
        mysqlClient.executeQuery("SELECT * FROM users");

        // PostgreSQL 사용
        DatabaseConnectionFactory postgresFactory = new PostgreSQLConnectionFactory();
        DatabaseClient postgresClient = new DatabaseClient(postgresFactory);
        postgresClient.executeQuery("SELECT * FROM products");
    }
}

3. 정리를 하자면..

1) 데이터베이스 연결 인터페이스 정의

  • DatabaseConnection 인터페이스를 생성합니다.
  • 이 인터페이스에 connect(), executeQuery(), close() 메서드를 선언합니다.

2) 구체적인 데이터베이스 연결 클래스 구현

  • MySQLConnectionPostgreSQLConnection 클래스를 만들어 DatabaseConnection 인터페이스를 구현합니다.
  • 각 클래스에서 인터페이스의 메서드들을 데이터베이스 특성에 맞게 구현합니다.

3) 데이터베이스 연결 팩터리 인터페이스 정의

  • DatabaseConnectionFactory 인터페이스를 생성합니다.
  • 이 인터페이스에 createConnection() 메서드를 선언합니다.

4) 구체적인 데이터베이스 연결 팩터리 클래스 구현

  • MySQLConnectionFactoryPostgreSQLConnectionFactory 클래스를 만들어 DatabaseConnectionFactory 인터페이스를 구현합니다.
  • 각 팩터리 클래스의 createConnection() 메서드에서 해당 데이터베이스 연결 객체를 생성하여 반환합니다.

5) 클라이언트 코드 작성

  • 클라이언트는 구체적인 데이터베이스 연결 클래스를 직접 생성하지 않습니다.
  • 대신 적절한 팩터리 객체를 사용하여 데이터베이스 연결을 얻습니다.
  • 클라이언트는 DatabaseConnection 인터페이스를 통해 데이터베이스 작업을 수행합니다.

장점

1. 느슨한 결합

  • 클라이언트 코드가 구체적인 클래스가 아닌 인터페이스에 의존하게 됩니다.
  • 이로 인해 시스템의 각 부분을 보다 독립적으로 변경하고 확장할 수 있습니다.

2. 단일 책임 원칙 준수

  • 객체 생성 로직을 팩터리 클래스로 분리하여 각 클래스의 책임을 명확히 합니다.
  • 클라이언트 코드는 객체 사용에만 집중할 수 있습니다.

3. 개방-폐쇄 원칙 준수

  • 새로운 유형의 객체를 추가할 때 기존 코드를 수정하지 않고 새로운 팩터리를 추가하기만 하면 됩니다.
  • 이는 코드의 확장성을 높이고 유지보수를 용이하게 합니다.

4. 코드 재사용성 향상

  • 동일한 팩터리 인터페이스를 사용하여 다양한 객체를 생성할 수 있습니다.
  • 이는 코드 중복을 줄이고 일관성을 유지하는 데 도움이 됩니다.

5. 테스트 용이성

  • 인터페이스를 통한 의존성 주입으로 단위 테스트 시 Mock 객체를 쉽게 사용할 수 있습니다.
  • 이는 테스트 주도 개발(TDD)을 용이하게 합니다.

6. 복잡성 관리

  • 객체 생성의 복잡성을 팩터리 클래스로 캡슐화하여 클라이언트 코드를 단순화합니다.
  • 이는 전체 시스템의 복잡성을 관리하는 데 도움이 됩니다.
profile
안녕 세계!
post-custom-banner

0개의 댓글