Search

Chapter 13. 추상 팩토리 패턴

생성일
2025/06/28 03:49
태그

추상 팩토리 패턴

추상 팩토리 패턴은 관련성 있는 여러 종류의 객체를 일관된 방식으로 생성하는 팩토리 클래스를 정의하는 패턴을 말한다.
이후의 예시에도 나오겠지만 Elevator는 클래스가 있고 엘레베이터를 구동하는 Motor와 Door 클래스가 있다고하면, 모터와 문을 구현하는 LG Motor, LG Door, Samsung Motor, Samsung Door 등의 여러 클래스들 중 어떤 것을 사용해도 문제가 없을 것이다. 하지만 일반적으로 같은 회사의 부품을 사용하기 때문에, LG Motor + LG Door를 생성해주는 LG Factory 클래스와 Samsung Motor + Samsung Door를 생성해주는 Samsung Factory 클래스를 두고 이를 추상화하여 간편하게 사용하도록 만든 것이 추상 팩토리 패턴이다.
추상 팩토리 패턴에서의 역할은 위와 같다.
AbstractFactory : 실제 팩토리 클래스의 공통 인터페이스
ConcreteFactory : 구현체 팩토리 클래스로 추상 메서드를 재정의하여 실제 부품의 생성자를 호출
AbstractProduct : 부품의 공통 인터페이스
ConcreteProduct : 부품의 구체 클래스
추상 팩토리 패턴의 순차 다이어그램은 위와 같다.

엘레베이터 예제로 추상 팩토리 패턴 이해하기

엘레베이터 설계하기

위에서 언급한 엘레베이터에 모터와 문을 어느 브랜드의 부품을 사용할 지 결정하는 예제를 보자.
모터와 문이 변경될 수 있기 때문에, 일반적으로 위와 같이 설계하게 될 것이다.
또한 OCP를 지키기 위해, 변경에 대한 대비로 이처럼 생성자 팩토리 패턴을 추가하게 될 것이다.
public abstract class Motor { private MotorStatus motorStatus; private Door door; public Motor() { motorStatus = MotorStatus.STOPPED; } public MotorStatus getMotorStatus() { return motorStatus; } public void setDoor(final Door door) { this.door = door; } public void move() { door.close(); motorStatus = MotorStatus.MOVING; moveMotor(); } private void setMotorStatus(final MotorStatus motorStatus) { this.motorStatus = motorStatus; } protected abstract void moveMotor(); } public class LGMotor extends Motor { @Override protected void moveMotor() { // LG Motor move } } public class HyundaiMotor extends Motor { @Override protected void moveMotor() { // Hyundai Motor move } }
Java
복사
public abstract class Door { private DoorStatus doorStatus; public Door() { this.doorStatus = DoorStatus.CLOSED; } public DoorStatus getDoorStatus() { return doorStatus; } public void open() { if (doorStatus == DoorStatus.OPEN) { return; } doOpen(); doorStatus = DoorStatus.OPEN; } public void close() { if (doorStatus == DoorStatus.CLOSED) { return; } doClose(); doorStatus = DoorStatus.CLOSED; } protected abstract void doOpen(); protected abstract void doClose(); } public class LGDoor extends Door { @Override protected void doOpen() { // open LG door } @Override protected void doClose() { // close LG door } } public class HyundaiDoor extends Door { @Override protected void doOpen() { // open Hyundai door } @Override protected void doClose() { // close Hyundai door } }
Java
복사
public enum VendorId { LG, HYUNDAI } public class MotorFactory { public static Motor createMotor(final VendorId vendorId) { return switch (vendorId) { case LG -> new LGMotor(); case HYUNDAI -> new HyundaiMotor(); }; } } public class DoorFactory { public static Door createDoor(final VendorId vendorId) { return switch (vendorId) { case LG -> new LGDoor(); case HYUNDAI -> new HyundaiDoor(); }; } }
Java
복사
public class Client { public static void main(String[] args) { final Door lgDoor = DoorFactory.createDoor(VendorId.LG); final Motor lgMotor = MotorFactory.createMotor(VendorId.LG); lgMotor.setDoor(lgDoor); lgDoor.open(); lgMotor.move(); } }
Java
복사
이를 코드로 작성하면 이와 같다.

문제점

위 코드에서 Motor와 Door의 브랜드를 Hyundai로 변경한다고 하면,
public class Client { public static void main(String[] args) { final Door hyundaiDoor = DoorFactory.createDoor(VendorId.HYUNDAI); final Motor hyundaiMotor = MotorFactory.createMotor(VendorId.HYUNDAI);); hyundaiMotor.setDoor(hyundaiDoor); hyundaiDoor.open(); hyundaiMotor.move(); } }
Java
복사
코드는 이와 같이 변경해야한다. 지금은 엘레베이터를 구성하는 부품이 Motor와 Door 둘 뿐이지만 부품이 8개가 더 들어간다고하면,
위의 8개의 Factory를 전부 만들고
public class Client { public static void main(String[) args) { Door hyundaiDoor = DoorFactory.createDoor(VendorlD.HYUNDAI); Motor hyundaiMotor = MotorFactory.createMotor(VendorlD.HYUNDAI); hyundaiMotor. setDoor(hyundaiDoor); ArrivalSensor hyundaiArrivalSensor = ArrivalSensorFactory.createArrivalSensor(VendorlD.HYUNDAI); WeightSensor hyundaiWeightSensor = WeightSensorFactory.createWeightSensor( VendorlD .HYUNDAI); ElevatorLamp hyundaiElevatorLamp = ElevatorLampFactory.createElevatorLamp(VendorlD.HYUNDAI); FloorLamp hyundaiFloorLamp = FloorLampFactory .createFloorLamp(VendorlD.HYUNDAI); DirectionLamp hyundaiDirectionLarnp = DirectionLampFactory.createDirectionLamp(VendorlD.HYUNDAI); Speaker hyundaiSpeaker = SpeakerFactory.createSpeaker(VendorlD.HYUNDAI); ElevatorButton hyundaiElevatorButton = ElevatorButtonFactory.createElevatorButton(VendorlD.HYUNDAI); FloorButton hyundaiFloorButton = FloorButtonFactory.createElevatorFloorButton(VendorlD.HYLJNDAI); hyundaiDoor.open(); hyundaiMotor.move(); } }
Java
복사
이처럼 수많은 코드를 작성하고 변경해야한다.
또한 Samsung이라는 새로운 밴더가 추가되는 경우에도 각 부품의 Factory를 찾아가
public class DoorFactory { public static Door createDoor(final VendorId vendorId) { return switch (vendorId) { case LG -> new LGDoor(); case HYUNDAI -> new HyundaiDoor(); case SAMSUNG -> new SamsungDoor(); }; } }
Java
복사
이와 같은 case문을 하나하나 추가해주어야 한다. 이는 설계적으로는 문제가 없지만 많은 코드를 변경해야하는 문제가 있다.

해결책

일반적인 경우라면 LG 모터를 사용할 때 LG의 문도 같이 사용하고, 현대 모터를 사용하면 현대의 문을 할 것이다. 이처럼 여러 종류의 객체를 생성할 때 생성하는 객체들 사이의 연관성이 있는 경우라면, 추상 팩토리 패턴을 도입하여 각 종류별로 별도의 Factory 클래스를 생성하는 대신 관련 객체를 일관성 있게 생성하게 만들 수 있다.
public interface ElevatorFactory { Motor createMotor(); Door createDoor(); } public class LGElevatorFactory implements ElevatorFactory { @Override public Motor createMotor() { return new LGMotor(); } @Override public Door createDoor() { return new LGDoor(); } } public class HyundaiElevatorFactory implements ElevatorFactory { @Override public Motor createMotor() { return new HyundaiMotor(); } @Override public Door createDoor() { return new HyundaiDoor(); } }
Java
복사
public class Client { public static void main(String[] args) { final ElevatorFactory factory = new LGElevatorFactory(); final Door door = factory.createDoor(); final Motor motor = factory.createMotor(); motor.setDoor(door); door.open(); motor.move(); } }
Java
복사
추상 팩토리 패턴을 적용하면 밴더를 변경해야하더라도 한 줄만 변경하면되고, 새로운 밴더를 추가하는 경우에도 ElevatorFactory를 구현하는 클래스 하나만 더 만들면 해결된다.
여기서 의존관계를 끊고 싶다면 팩토리 메서드 패턴을 추가해도 좋고, 각 Factory의 구현 클래스에 싱글톤 패턴을 적용하는 것도 권장된다.