추상 팩토리(Abstract factory) 패턴

weekbelt·2022년 11월 30일
0

1. 패턴 소개

추상 팩토리 패턴은 관련있는 여러 인스턴스를 만들어주는 팩토리를 추상화된 형태로 정의한 패턴입니다. 인터페이스로 정의하거나 추상클래스로 하거나 구체적인 팩토리에서 구체적인 인스턴스를 만드는것 까지는 팩토리메서드 패턴과 굉장히 비슷하지만 추상 팩토리 패턴은 초점이 클라이언트에 있습니다. 추상 팩토리패턴의 목적 자체가 팩토리에서 인스턴스를 만들어서 쓰는 코드를 인터페이스 기반으로 코딩을 할 수 있도록 도와주는 패턴입니다.

위의 그림이 약간 복잡해 보이지만 팩토리 메서드 패턴에 비해 클라이언트만 추가되었습니다.

public class SedanFactory extends DefaultCarFactory {

    @Override
    public Car createCar() {
        Car car = new Sedan();
        car.setBody(new FBody());
        car.setWheel(new SpokeWheel());
        return car;
    }
}

위의 SedanFactory클래스에서 createCar메서드를 호출할때 body는 FBody로 wheel은 SpokeWheel로 구체적인 클래스타입으로 설정하고 있습니다. 만약에 비슷한 제품군, 예를 들어 Body는 GBody, Wheel은 PinWheel등이 추가 되었을 때 이것들로 바꿔줘야 할 때 createCar에 setBody, setWheel메서드에 값을 바꿔서 설정해야 합니다. 이는 결국 변경에 닫힌코드가 아니게 됩니다. 이 부분이 바뀌지 않으면서 제품군을 늘려나갈 수 있게 추상 팩토리 패턴을 적용해서 구현해보도록 하겠습니다.

2. 패턴 적용

추상 팩토리 패턴을 적용해서 아래와 같이 setBody, setWheel에 구체적인 클래스에 의존하지 않도록 변경해보겠습니다.

public class SedanFactory extends DefaultCarFactory {

    @Override
    public Car createCar() {
        Car car = new Sedan();
        car.setBody(new FBody());
        car.setWheel(new SpokeWheel());
        return car;
    }
}

가장먼저 FBody와 SpokeWheel의 제품군을 일반화시킨 인터페이스를 먼저 정의합니다.

Body 제품군

public interface Body {
}

Wheel 제품군

public interface Wheel {
}

Body제품군과 Wheel제품군을 생산하는 추상 팩토리

public interface CarPartsFactory {

    Body createBody();

    Wheel createWheel();
}

각 제품군의 인터페이스와 그 제품군들을 생산하는 추상 팩토리를 만들었으니 추상팩토리타입의 구체적인 팩토리를 생성합니다.

Sedan을 생성하는 팩토리

public class SedanPartsFactory implements CarPartsFactory {

    @Override
    public Body createBody() {
        return new FBody();
    }

    @Override
    public Wheel createWheel() {
        return new SpokeWheel();
    }
}

Sedan을 생성하는 팩토리는 body는 FBody wheel은 SpokeWheel로 생성하도록 하겠습니다. 그러려면 각 제품군에 해당하는 인터페이스를 구현해야합니다.

Body제품군의 FBody

public class FBody implements Body {
}

Wheel제품군의 SpokeWheel

public class SpokeWheel implements Wheel{
}

Sedan을 생성하는 팩토리를 구현했으니 사용해 봅시다.

public class SedanFactory extends DefaultCarFactory {

    private CarPartsFactory carPartsFactory;

    public SedanFactory(CarPartsFactory carPartsFactory) {
        this.carPartsFactory = carPartsFactory;
    }

    @Override
    public Car createCar() {
        Car car = new Sedan();
        car.setBody(carPartsFactory.createBody());
        car.setWheel(carPartsFactory.createWheel());
        return car;
    }

}

CarPartsFactory추상 팩토리를 주입받아서 추상 팩토리를 통해서 Body와 Wheel을 가져오도록 하면 SedanFactory의 코드는 이제 바뀔일이 없습니다. 만약에 이상태에서 프리미엄 제품군을 만든다고 하면 추상 팩토리를 적용하기전에는 setBody, setWheel에 구체적인 클래스를 추가해서 변경해야 했지만 지금은 추상팩토리를 구현하는 새로운 팩토리를 SedanFactory에 주입하기만하면 됩니다. 한번 적용해 보겠습니다.

Premium FBody

public class FBodyPremium implements Body {
}

프리미엄 FBody도 Body제품군 입니다.

Premium SpokeWheel

public class SpokeWheelPremium implements Wheel{
}

프리미엄 SpokeWheel도 Wheel제품군 입니다.

이제 이 Premium제품군을 생산할 팩토리를 생성하겠습니다.

Premium 팩토리

public class SedanPremiumPartsFactory implements CarPartsFactory {

    @Override
    public Body createBody() {
        return new FBodyPremium();
    }

    @Override
    public Wheel createWheel() {
        return new SpokeWheelPremium();
    }
}

Premium Body와 Wheel을 생산하는 팩토리를 구현하였습니다. 이 팩토리를 SedanFactory에 주입받기만하면 Premium제품을 생산할 수 있습니다.

public class SedanFactory extends DefaultCarFactory {

    private CarPartsFactory carPartsFactory;

    public SedanFactory(CarPartsFactory carPartsFactory) { // 주입
        this.carPartsFactory = carPartsFactory;
    }

    @Override
    public Car createCar() {
        Car car = new Sedan();
        car.setBody(carPartsFactory.createBody());
        car.setWheel(carPartsFactory.createWheel());
        return car;
    }

}

실제로 SedanFactory에 SedanPremiumPartsFactory인스턴스를 주입해서 생성한 자동차에 어떤 부품이 들어있는지 확인해 보겠습니다.

public class CarInventory {

    public static void main(String[] args) {
        SedanFactory sedanFactory = new SedanFactory(new SedanPremiumPartsFactory());
        Car car = sedanFactory.createCar();
        System.out.println(car.getBody().getClass().getSimpleName());
        System.out.println(car.getWheel().getClass().getSimpleName());
    }
}

실행 결과 생성한 Car의 부품이 Premium부품이라는 것을 확인할 수 있습니다.

실행결과

FBodyPremium
SpokeWheelPremium

어떤 제품군을 제공하는 팩토리를 SedanFactory에 주입해주느냐에 따라서 결과가 달라지지 SedanFactory자체의 코드가 변경되지는 않습니다. SedanFactory클래스 입장에서는 코드변경이 없고 주입 받는 팩토리가 다를때마다 다른결과가 보여지기 때문에 확장에 대해서 열려있고 변경에는 닫혀있는 객체지향 원칙이 지켜짐을 알 수 있습니다.

3. 팩토리 메소드 패턴과의 비교

공통점

둘 다 구체적인 객체 생성 과정을 추상화한 인터페이스를 제공합니다.

차이점

팩토리 메소드 패턴은 "팩토리를 구현하는 방법 (inheritance)"에 초점을 둬서 구체적인 객체 생성 과정을 하위 또는 구체적인 클래스로 옮기는 것이 목적입니다.

추상 팩토리 패턴은 "팩토리를 사용하는 방법 (composition)"에 초점을 둬서 관련있는 여러 객체를 구체적인 클래스에 의존하지 않고 만들 수 있게 해주는 것이 목적입니다.

참고

profile
백엔드 개발자 입니다

0개의 댓글