[객체 생성 패턴] Chapter 3-2. Abstract Factory Pattern : 패턴 적용하기

 

✍️ 추상 팩토리 패턴, 적용하기

먼저, 이전 포스팅에서 살펴봤던 ShipFactory의 구현체 TurtleshipFactory 클래스가 현재 구조에서 Client에 해당한다.

 

TurtleshipFactory의 createShip 메서드를 보면 구체적인 제품의 인스턴스(TurtleAnchor, TurtleEngine)를 주입하고 있다. 이처럼 클라이언트에서 구체적인 인스턴스를 주입하는 것이 아닌 팩토리로부터 제품(Anchor, Engine ...)을 생성하고 사용하는 코드를 인터페이스 기반으로 작성하게끔 하는 것이 추상 팩토리 패턴의 목적이다.

public class TurtleshipFactory implements ShipFactory {
    @Override
    public Ship createShip() {
        Ship ship = new Turtleship();
        ship.anchor = new TurtleAnchor();
        ship.engine = new TurtleEngine();

        return ship;
    }
}

 

본격적으로 추상 팩토리를 만들어보면, 인터페이스를 하나 생성하고 두 개의 추상 메서드를 정의하자. 하나는 Anchor를 반환하고 나머지 하나는 Engine을 반환한다. 심플하게도 이게 추상 팩토리의 전부다.

public interface ShipPartsFactory {
    Anchor createAnchor();

    Engine createEngine();
}
public class Anchor {
}
public class Engine {
}

 

인터페이스 기반의 추상 팩토리를 만들었으니 실제로 제품(Anchor, Engine)을 만드는 콘크리트 팩토리를 작성한다. 이로써 너무나 당연한 사실이지만 ShipPartsFactory의 콘크리트 팩토리는 Anchor와 Engine을 상속 혹은 구현한(일련의 규약을 지키는) 객체만을 반환하는 팩토리가 된다. 

public class TurtleshipPartsFactory implements ShipPartsFactory{

    @Override
    public Anchor createAnchor() {
        return new TurtleAnchor();
    }

    @Override
    public Engine createEngine() {
        return new TurtleEngine();
    }
}
public class TurtleAnchor extends Anchor {
}
public class TurtleEngine extends Engine {
}

 

이제 Client 코드에 적용시켜 보자.

더 이상 클라이언트 코드에서 구체적인 제품의 인스턴스를 주입하는 게 아닌 추상 팩토리로부터 객체를 주입받기 때문에 Turtleship의 구체적인 Anchor와 Engine이 변경되더라도 구체적인 제품을 생성하는 콘크리트 팩토리만 교체하면 되므로 클라이언트 코드가 수정될 일은 없다. 

public class TurtleshipFactory implements ShipFactory {
    private ShipPartsFactory shipPartsFactory;

    public TurtleshipFactory(ShipPartsFactory shipPartsFactory) {
        this.shipPartsFactory = shipPartsFactory;
    }

    @Override
    public Ship createShip() {
        Ship ship = new Turtleship();
        ship.anchor = shipPartsFactory.createAnchor();
        ship.engine = shipPartsFactory.createEngine();

        return ship;
    }
}

 

가령 예를 들어 개선된 닻과 엔진이 생겼다면 아래와 같은 추상 팩토리를 하나 정의해서 TurtleshipFactory의 생성자에 주입만 해주면 개선된 닻과 엔진을 장착한 Turtleship이 된다.

public class TurtleshipGoodPartsFactory implements ShipPartsFactory {
    @Override
    public Anchor createAnchor() {
        return new TurtleGoodAnchor();
    }

    @Override
    public Engine createEngine() {
        return new TurtleGoodEngine();
    }
}
public class TurtleGoodAnchor extends Anchor {
}
public class TurtleGoodEngine extends Engine {
}

 

🍊 추상 팩토리, 주입하기

외부에서 TurtleShipFactory의 객체를 만들 때 어떤 추상 팩토리를 주입하냐에 따라 turtleship의 부품이 다른 것을 확인할 수 있다. 

public class ShipInventory {
    public static void main(String[] args) {
        TurtleshipFactory turtleshipFactory = new TurtleshipFactory(new TurtleshipPartsFactory());
        Ship turtleShip = turtleshipFactory.createShip();
        System.out.println(turtleShip.anchor.getClass());

        TurtleshipFactory turtleshipGoodFactory = new TurtleshipFactory(new TurtleshipGoodPartsFactory());
        Ship turtleShipGood = turtleshipGoodFactory.createShip();
        System.out.println(turtleShipGood.anchor.getClass());
    }
}
class _03_AbstractFactory.Model.Parts.TurtleAnchor
class _03_AbstractFactory.Model.Parts.TurtleGoodAnchor

 

🐢 정리

핵심은 제품과 제품을 생산하는 팩토리가 인터페이스 기반으로 작성됐다는 점과 클라이언트 코드 또한 인터페이스 기반으로 작성돼 특정 객체에 의존하지 않고 제품이 추가되어도 코드의 수정이 없다는 것이다.  

 

 

 

인프런의 백기선님의 강의 코딩으로 학습하는 GoF의 디자인 패턴을 참고해서 작성했습니다.

 

코딩으로 학습하는 GoF의 디자인 패턴 - 인프런 | 강의

디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를 개발할

www.inflearn.com