본문 바로가기

Computer Science/Design Pattern

[Design Pattern] Abstract Factory pattern

반응형

1. Abstract Factory pattern

추상 팩토리는 관련 객체들의 구현 클래스들을 지정하지 않고도 관련된 객체들의 모음을 생성할 수 있도록 인터페이스를 제공하는 생성 패턴이다.

 

이름을 들으면 마치 팩토리 메서드 패턴과 유사하다고 느낄 수 있다. 이 두 방식은 실제 객체를 생성하는 로직을 팩토리에 위임한다는 점에서 유사함을 가지고 있지만 명확한 차이도 있다. 팩토리 메서드 패턴의 경우 조건에 따라서 다른 객체를 생성하는데, 이때 조건을 확인하고 알맞는 객체를 생성하는 로직이 팩토리 클래스에 위임된다. 반면에 추상 팩토리 패턴은 서로 관련이 있는 객체들을 묶어서 팩토리 클래스로 만들고 이러한 팩토리들을 조건에 따라 생성하도록 다시 팩토리를 만들어서 객체를 생성하는 패턴이다. 항상 그런것은 아니지만 팩토리 메서드 패턴을 좀 더 캡슐화한 방식이 추상 팩토리 패턴이라고 이해할 수 있다.

- why abstract factory?

추상 팩토리 패턴을 사용하는 경우를 이해하기 쉽게 예시와 함께 알아보자.

 

우리가 가구를 만드는 코드를 작성한다고 생각해보자. 가구 중에는 의자도 있고, 탁자나 소파도 있을 것이다. 이러한 가구들을 만들때 같은 가구여도 스타일에 따라서 결과물이 달라지게 된다. 모던한 스타일의 가구를 만들 수 있고, 또는 클래식한 스타일의 가구를 만들 수도 있다. 대부분의 경우에는 인테리어의 테마에 따라서 같은 스타일의 가구들로 구성한다. 그렇기 때문에 모던한 가구들과 클래식한 가구들을 구분해서 가구를 구성할 수 있도록 로직을 짜야한다.

 

만약에 팩토리 메서드 패턴을 사용한다고 생각해보자. 각각의 가구들이 모두 스타일별로 분기를 만들어서 각 스타일의 가구를 만들 수 있도록 할 수 있다. 예를들어 의자 클래스는 스타일 조건에 따라서 모던 스타일 의자 또는 클래신 스타일 의자를 만들도록 로직을 구현한다. 다른 가구인 탁자나 침대, 소파 등도 동일하다.

 

하지만 팩토리 메서드 패턴의 경우에는 새로운 스타일이 추가될 때 기존의 코드를 수정해야 하는 문제가 발생한다. 만약 새로운 스타일이 추가되면 각 가구 클래스의 메서드들에 분기를 추가하고 해당 스타일로 가구를 생성하는 로직을 추가하는 코드 수정 작업을 해야한다.

 

추상 팩토리 패턴을 사용한다면 보다 간단하게 코드를 유지보수 할 수 있다. 앞에서 말한 것처럼 가구를 구성할 때는 같은 스타일의 가구들로 구성하게 된다. 그렇기 때문에 가구의 스타일별로 가구를 구성하는 팩토리를 만들고 이를 사용해서 각 스타일에 해당하는 가구들을 만들도록 할 수 있다. 이렇게 하면 각 가구들의 팩토리 클래스는 필요 없어진다. 대신 가구 전체에 대한 팩토리 인터페이스와 이를 구현한 각 가구 스타일 팩토리 클래스들을 구현하면 된다.

2. 클래스 구조

https://refactoring.guru/ko/design-patterns/abstract-factory

 

추상 팩토리 패턴의 클래스 구조는 위의 사진과 같다.

 

앞의 가구 구현 로직과 비교한다면 팩토리 메서드의 결과인 product 들은 chair, table, sofa 등의 가구이다. 이때 가구들의 스타일에 따라서 실제 결과물로 출력되는 Concrete Product modern chair 또는 classic chair 된다.

 

실제 가구를 생성하는 팩토리는 ModernFurnitureFactory, ClassicFurnitureFactory 가 된다. 이들은 client 가 가구들을 생성할 수 있도록 createFurniture(), createChair(), createTable() 등의 메서드를 가지고 있어야 한다. 이러한 틀을 AbstractFactory 인터페이스에 정의해야 한다. 예제에서는 FurnitireFactory 라는 팩토리 인터페이스를 통해서 각 가구를 생성하는 메서드를 선언해놓을 것이다.

3. 예제코드

예제 코드는 위의 설명에서 예시를 들었던 스타일에 따른 가구를 구성하는 가구 팩토리 코드를 구현할 것이다. 예시로 가구들 중 의자를 생성할 것이고 스타일은 모던풍과 클래식풍으로 만들 수 있도록 구현할 것이다.

- Product

우선은 product 인 의자의 계층구조 코드를 구현해보자. 의자의 계층구조는 Abstract Product 인 인터페이스 Chair 와 이를 각각의 스타일에 따라 구현한 Concrete Product 인 ModernChair, ClassicChair 클래스로 구성된다.

 

/*
 * Chair
 * - Abstract product
 */
public interface Chair {

    public void printName();
   
}

/*
 * ClassicChair
 * - Concrete product
 * - Classic style chair
 */
public class ClassicChair implements Chair {

    public ClassicChair() {
        System.out.println("Create new classic chair.");
    }

    @Override
    public void printName() {
        System.out.println("Classic chair");
    }
   
}

/*
 * ModernChair
 * - Concrete product of Chair
 * - Modern style chair
 */
public class ModernChair implements Chair {

    public ModernChair() {
        System.out.println("Create new modern chair.");
    }
  

    @Override
    public void printName() {
        System.out.println("Modern chair");
    }

}

- Factory

팩토리는 팩토리들의 구조를 선언한 인터페이스인 Abstract Factory 와 인터페이스를 각 제품의 생성 로직에 맞춰 구현한 Concrete Factory 로 구분된다. 이 예제에서는 가구 생성 팩토리 인터페이스 FurnitureFactory 와 인터페이스를 각각 Modern 과 Classic 스타일로 구현한 ModernFurnitureFactory 와 ClassicFurnitureFactory 클래스로 구성된다.

 

/*
 * FurnitureFactory
 * - Abstract factory
 */
public interface FurnitureFactory {

    public Chair createChair();

}

/*
 * ClassicFurnitureFactory
 * - Concrete factory
 * - factory for classic style furnitures
 */
public class ClassicFurnitureFactory implements FurnitureFactory {

    @Override
    public Chair createChair() {
        return new ClassicChair();
    }

}

/*
 * ModernFurnitureFactory
 * - Concrete factory
 * - factory for modern style furnitures
 */
public class ModernFurnitureFactory implements FurnitureFactory {

    @Override
    public Chair createChair() {
        return new ModernChair();
    }

}

- Client

클라이언트 코드는 위에서 생성된 Factory 코드를 사용하여 가구를 생성하는 코드이다. 로직 상에서 어떻게 사용하느냐에 따라서 다르게 구현될 수 있는데, 이 예제에서는 외부에서 팩토리를 입력받고 해당 팩토리를 사용하여 가구 구성을 생성하는 기능을 가지도록 구현하였다.

 

public class Client {
   
    private FurnitureFactory factory;

    public Client(FurnitureFactory factory) {
        this.factory = factory;
    }

    public FurnitureSet createFurnitureSet() {
        Chair chair = factory.createChair();
        Table table = factory.createTable();
        return new FurnitureSet(chair, table);
    }

}

class FurnitureSet {
   
    private Chair chair;
    private Table table;

    public FurnitureSet(Chair chair, Table table) {
        this.chair = chair;
        this.table = table;
    }

    public void printFurnitures() {
        System.out.println("----Print name of furnitures----");
        chair.printName();
        table.printName();
        System.out.println("--------------------------------\n");
    }

}

 

Client 는 외부에서 입력된 factory 를 가지고 있다가 사용자가 createFurnitureSet() 메서드를 호출하면 해당 팩토리에서 생성된 가구들을 담은 FurnitureSet 객체를 반환한다. FurnitureSet 은 같은 스타일의 가구들로 이루어진 가구 집합 클래스로 예제 코드에서 구현된 Chair 와 에제 코드에서는 제외된 Table 두개의 가구 객체를 가지고 있다.

- test code

public class FurnitureFactoryTest {

    @Test
    public void furnitureFactoryTest() {
        Client client;
        FurnitureSet furnitureSet;

        // classic style furniture
        client = new Client(new ClassicFurnitureFactory());
        furnitureSet = client.createFurnitureSet();
        furnitureSet.printFurnitures();

        // modern style furniture
        client = new Client(new ModernFurnitureFactory());
        furnitureSet = client.createFurnitureSet();
        furnitureSet.printFurnitures();
    }
}

 

테스트 코드에서는 각각 ClassicFurtnitureFactory 와 ModernFurnitureFactory 를 입력받은 client 로 furnitureSet 을 생성하고 이들의 정보를 출력하도록 하였다.

 

Create new classic chair.
Create new classic table.

----Print name of furnitures----
Classic chair
Classic table
--------------------------------

Create new modern chair.
Create new modern table.

----Print name of furnitures----
Modern chair
Modern table
--------------------------------

 

테스트 코드의 실행 결과는 위와 같아. 각각 ClassicFurnitureFactory 를 사용한 client 는 Classic chair 와 Classic table 을 ModernFurnitureFactory 를 사용한 client 는 Modern chair 와 Modern table 을 생성하는 것을 볼 수있다.

[Reference]

- https://refactoring.guru/design-patterns/abstract-factory

 

Abstract Factory

Solution The first thing the Abstract Factory pattern suggests is to explicitly declare interfaces for each distinct product of the product family (e.g., chair, sofa or coffee table). Then you can make all variants of products follow those interfaces. For

refactoring.guru

- https://victorydntmd.tistory.com/300

 

[디자인패턴] 추상 팩토리 패턴 ( Abstract Factory Pattern )

추상 팩토리 패턴 ( Abstract Factory Pattern )추상 팩토리 패턴이라는 이름만 봐서는 팩토리 메서드 패턴과 비슷해보이지만, 명확한 차이점이 있습니다. 팩토리 메서드 패턴조건에 따른 객체 생성을

victorydntmd.tistory.com

 

반응형

'Computer Science > Design Pattern' 카테고리의 다른 글

[Design Pattern] Builder Pattern  (0) 2023.01.04