추상 팩토리 패턴이란?
- 서로 관련된 여러 객체를 만들어주는 인터페이스를 제공하고 이를 구현하는 패턴
- 관련이 깊은 여러 종류의 객체를 일관된 방식으로 생성하는 경우에 유용하다.
- 팩토리 메서드 패턴 과 굉장히 비슷하다.
- 팩토리 메서드 패턴 은 하나의 객체 생성을 어떻게 할까에 집중하는 반면, 추상 팩토리 패턴은 관련 있는 여러 객체를 묶어 여러 구체적 클래스에 의존하지 않는 것에 집중한다.
다이어그램으로 살펴보기
AbstractFactory
는CreateProductA()
와CreateProductB()
라는 추상 메서드를 제공한다.ProductA
와ProductB
를 생성한다.- 구체적인 객체 생성 방법의 구현은 하위 클래스에게 맡긴다.
ConcreteFactory1
과ConcreteFactory2
는ProductA
와ProductB
객체를 생성할 구체적인 생성 코드를 가지고 있다.
현실 세계와의 비유: 자동차 공장
자동차
라는 완제품에엔진
과타이어
는 반드시 필요하다.- 두 객체는 서로 관련이 깊다고 볼 수 있다.
- 두 부품이 모두 같은 회사의 공장에서 나온다.
- 두 부품외 다른 종류의 부품은 모든 차가 같은 것을 쓴다고 가정하자.
디자인 패턴이 적용되지 않은 코드 살펴보기
public class SonataPartsFactory {
public CVVLEngine createCVVLEngine() {
return new CVVLEngine();
}
public HankookTire createHankookTire() {
return new HankookTire();
}
}
public class K9PartsFactory {
public V8Engine createV8Engine() {
return new V8Engine();
}
public MichelinTire createMichelinTire() {
return new MichelinTire();
}
}
@AllArgsConstructor
@ToString
public class Car {
private final Engine engine;
private final Tire tire;
}
public class Client {
public static void main(String[] args) {
K9PartsFactory k9PartsFactory = new K9PartsFactory();
SonataPartsFactory sonataPartsFactory = new SonataPartsFactory();
// create k9 car parts.
V8Engine v8Engine = k9PartsFactory.createV8Engine();
MichelinTire michelinTire = k9PartsFactory.createMichelinTire();
System.out.println("k9 엔진 : " + v8Engine);
System.out.println("k9 타이어 : " + michelinTire);
System.out.println("k9 생산: " + new Car(v8Engine, michelinTire));
// create sonata car parts.
CVVLEngine cvvlEngine = sonataPartsFactory.createCVVLEngine();
HankookTire hankookTire = sonataPartsFactory.createHankookTire();
System.out.println("쏘나타 엔진 생산: " + cvvlEngine);
System.out.println("쏘나타 타이어 생산: " + hankookTire);
System.out.println("쏘나타 생산: " + new Car(cvvlEngine, hankookTire));
}
}
Sonata
와K9
부품 공장의 구현 예제이다.- 자동차에 필요한 부품인 엔진과 타이어를 모두 구체적으로 생성하고 있다.
- 반환 타입과 메서드명이 모두 구체적이라서 생성 객체가 바뀌어야 하면 모든 코드도 같이 바뀌어야 한다.
...PartsFactory
코드와 이를 가져다 쓰는Client
코드가 같이 바뀌어야 한다.
디자인패턴 적용하기
- 추상 팩토리 패턴을 적용해보자.
추상 팩토리 만들기
public interface Engine {
}
public interface Tire {
}
public interface CarPartsFactory {
Engine createEngine();
Tire createTire();
}
Sonata
와K5
를Car
라는 단어로 추상화하여 추상 팩토리를 만들었다.CVVLEngine
과HankookTire
등도Engine
과Tire
로 추상화하였다.
- 추상 팩토리는 연관된 객체를 생성하는 추상 메서드를 가지고 있는 인터페이스이다.
- 자동차를 만드는데 필요한 부품을 모아놨으므로 코드의 응집도에도 좋다.
추상 팩토리 클라이언트 클래스 만들기
public class CarFactory {
private final CarPartsFactory carPartsFactory;
public CarFactory(CarPartsFactory carPartsFactory) {
this.carPartsFactory = carPartsFactory;
}
public Car createCar() {
return new Car(
carPartsFactory.createEngine(),
carPartsFactory.createTire()
);
}
}
CarPartsFactory
를 주입받아 사용하는 클래스이다.- 이 클래스는 어떤 엔진과 어떤 타이어를 가진 자동차를 만들더라도 절대 변할 일이 없다.
디자인 패턴이 적용되지 않았던 코드 개선하기
public class K9PartsFactory implements CarPartsFactory{
@Override
public Engine createEngine() {
return new V8Engine();
}
@Override
public Tire createTire() {
return new MichelinTire();
}
}
public class SonataPartsFactory implements CarPartsFactory {
@Override
public Engine createEngine() {
return new CVVLEngine();
}
@Override
public Tire createTire() {
return new HankookTire();
}
}
CarPartsFactory
인터페이스를 상속하도록 바뀌었다.- 추상 팩토리의 구현체이다.
클라이언트 코드 개선하기
public class Client {
public static void main(String[] args) {
CarFactory k9Factory = new CarFactory(new K9PartsFactory());
CarFactory sonataFactory = new CarFactory(new SonataPartsFactory());
System.out.println("K9 생산 : " + k9Factory.createCar());
System.out.println("Sonata 생산 : " + sonataFactory.createCar());
}
}
CarFactory
를 생성하고 부품 공장만 주입해주면, 자동차 공장을 만들 수 있게 되었다.- 앞으로 어떠한 새로운 차가 나오더라도
CarFactory
클래스는 변할 일이 없다. - 새로운 차가 나온다면, 어떤 엔진을 사용하고 어떤 타이어를 사용하는지를 명시하는
CarPartsFactory
를 상속한 구현 클래스를 만들어주기만 하면 된다.CarFactory
나CarPartsFactory
등을 수정할 필요가 없는 점은 변경에 닫혀있다고 볼 수 있다.- 새로운 차를 추가하기 쉽다는 점은 확장에 열려있다고 볼 수 있다.
- 다만,
CarPartsFactory
는 관점에 따라 하나의 클래스에서 여러가지 일을 한다고 볼 수도 있다.EngineFactory
,TireFactory
등으로 더 나뉘어도 되긴 한다.
다이어그램 살펴보기
팩토리 메서드 패턴과 비교해보기
- 둘 다 객체를 만드는 과정을 추상화시킨 것이다.
- 둘은 관점이 다르다.
- 팩토리 메서드 패턴은
팩토리를 구현하는 방법(inheritance)
에 초점을 둔다.- 하나의 팩토리 메서드를 어떻게 구현할 것인가?
- 추상 팩토리 패턴은
팩토리를 사용하는 방법(composition)
에 초점을 둔다.- 관련 있는 객체들을 어떻게 생성할 것인가?
- 팩토리 메서드 패턴은
- 둘은 목적이 다르다.
- 팩토리 메서드 패턴은 구체적인 객체 생성 과정을 하위 또는 상위 클래스로 옮기는 것이 목적
ConcreteClass
를 구현하는 것에 목적을 둠- 상속의 관점
- 추상 팩토리 패턴은 관련있는 여러 객체를 구체적인 클래스에 의존하지 않고 만들 수 있게 하는 것이 목적
ConcreteClass
구현 자체보다는 클라이언트에서 추상화된 팩토리를 사용하는 것에 더 초점을 맞춤- 구성의 관점
- 팩토리 메서드 패턴은 구체적인 객체 생성 과정을 하위 또는 상위 클래스로 옮기는 것이 목적
추상 팩토리 패턴의 용례
추상 팩토리 패턴의 용례에 대해 알아보자.
DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File("src/main/resources/config.xml"));
System.out.println(document.getDocumentElement());
DocumentBuilderFactory
추상 클래스는 추상 메서드인newDocumentBuilder()
를 통해DocumentBuilder
를 제공하는 것에 관심이 있다.- 팩토리 메서드 패턴과 비교하자면, 어떻게 제공 메서드를 구현할 것이냐보다도 그냥 어떤 종류의
DocumentBuilder
를 제공할 것이냐에 관심이 있다. - 이와 비슷한 종류로
XPathFactory
,TransformerFactory
가 있다.
FactoryBean
FactoryBean
은 스프링의 인터페이스로 보통new
키워드로 간단히 생성하기 힘든 객체가 있을 때 이를 이용한다.
public class CarFactoryBean implements FactoryBean<Car> {
private final CarFactory carFactory = new CarFactory(new K9PartsFactory());
@Override
public Car getObject() throws Exception {
return carFactory.createCar();
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
}
@Configuration
public class FactoryBeanConfig {
@Bean
public CarFactoryBean carFactory() {return new CarFactoryBean();}
}
public class FactoryBeanExample {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(FactoryBeanConfig.class);
Car bean = applicationContext.getBean(Car.class);
System.out.println(bean);
}
}
CarFactoryBean
의 구현을 보면,Bean
으로 등록되기 위해 필요한 메서드들이 몰려있는 것을 볼 수 있다.getObject()
와getObjectType()
을 꼭 구현해야 한다.FactoryBean
인터페이스는 어떤 메서드들의 구현을 조합해야Bean
을 생성할 수 있는지에 관심이 있다.
- 실제로
FactoryBeanConfig
에서 등록한Bean
은CarFactoryBean
인데 애플리케이션 컨텍스트에서 등록한적 없는Car
타입의 빈을 잘 찾아낸다.- 스프링이
FactoryBean
을 인식하고FactoryBean
의 로직에 따라Bean
을 잘 만들어주었기 때문이다.
- 스프링이
레퍼런스
https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4/dashboard
반응형
'Java > 자바 디자인 패턴' 카테고리의 다른 글
프로토타입 패턴 (Prototype Pattern) 이란? (2) | 2023.01.28 |
---|---|
빌더 패턴 (Builder Pattern) 이란? (0) | 2023.01.26 |
팩토리 메서드 패턴 (Factory Method Pattern) 이란? (0) | 2023.01.23 |
싱글톤 패턴 (Singleton Pattern) 이란? (0) | 2023.01.20 |
자바 믹스인(mixins)이란? (0) | 2021.12.30 |