팩토리 메서드 패턴 VS 추상 팩토리 메서드 패턴
- 팩토리 메서드 패턴 : 조건에 따른 객체 생성을 팩토리 클래스로 위임하여, 팩토리 클래스에서 객체를 생성하는 패턴
- 추상 팩토리 메서드 패턴 : 서로 관련있는 객체들을 다 모아 팩토리 클래스를 만들고, 이들 팩토리를 조건에 따라 생성하도록 다시 팩토리를 만들어 객체를 생성하는 패턴
간단히 말하면, 팩토리 메서드 패턴을 좀 더 캡슐화한 패턴이다. 하지만 주의해야 할 것은 추상 메서드 팩토리 패턴이 팩토리 메서드 패턴의 상위호환은 아니며, 상황에 따라 적절히 사용해야한다.
예를 들어, 회사A(CompanyA)와 회사B(CompanyB)에서 마우스(Mouse)와 스피커(Speaker)를 만든다고 가정해보자. (팩토리 메서드 패턴 적용)
//Mouse 인터페이스 public interface Mouse{} //CompanyA에서 만드는 Mouse를 만드는 클래스 정의 public class MouseA implements Mouse { public MouseA() { System.out.println("MouseA 생성"); } } //CompanyB에서 만드는 Mouse를 만드는 클래스 정의 public class MouseB implements Mouse { public MouseB() { System.out.println("MouseB 생성"); } //Mouse를 만드는 Factory클래스 정의 public class MouseFactory { public Mouse createMouse (String companyName) { //Mouse 객체 생성 Mouse mouse = null; //Company에 따른 조건문 생성 swich (companyName) { //CompanyA case : "CompanyA" : mouse = new MouseA(); break; //CompanyB case : "CompanyB" : mouse = new MouseB(); break; } //Mouse 반환 return mouse; } } //Speaker 인터페이스 public interface Speaker{} //CompanyA에서 만드는 Speaker 만드는 클래스 정의 public class SpeakerA implements Speaker { public SpeakerdA() { System.out.println("SpeakerA 생성"); } } //CompanyB에서 만드는 Speaker를 만드는 클래스 정의 public class SpeakerB implements Speaker { public SpeakerB() { System.out.println("SpeakerB 생성"); } //Speaker 만드는 Factory클래스 정의 public class SpeakerFactory { public Speaker createSpeaker (String companyName) { //Speaker 객체 생성 Speaker speaker = null; //Company에 따른 조건문 생성 swich (companyName) { //CompanyA case : "CompanyA" : speaker = new SpeakerA(); break; //CompanyB case : "CompanyB" : speaker = new SpeakerB(); break; } //Speaker 반환 return speaker; } }
다음으로 MouseFactory와 SpeakerFactory의 객체를 생성해서 어떤 Company에서 Mouse와 Speaker를 생산할지 결정할 ComputerFactory 클래스를 정의한다.(팩토리 메서드 패턴 적용)
//ComputerFactory 클래스 정의 public class ComputerFactory { //Mouse와 Speaker를 이용해 Computer를 만든는 메서드 public void createComputer(String companyName) { //Mouse와 Speaker를 만드는 클래스 객체 생성(Factory클래스 객체 생성) MouseFactory mousefactory = new MouseFactory(); SpeakerFactory speakerfactory = new SpeakerFactory(); //Mouse와 Speaker를 만드는 메서드 실행 mousefactory.createMouse(companyName); speakerfactory.createSpeaker(companyName); System.out.println("====Computer" + companyName + " 완성===="); } }
마지막으로 컴퓨터를 만드는 Company클래스를 정의한다(main 클래스 정의)
//Company 클래스 생성 public class Company { public static void main(String args[]) { ComputerFactory computerFactory = new ComputerFactory(); computerFactory.createComputer("A"); System.out.println("===구분선==="); computerFactory.createComputer("B"); } } //결과값 MouseA 생성 SpeakerA 생성 ===ComputerA 완성=== ===구분선=== MouseB 생성 SpeakerB 생성 ===ComputerB 완성===
하지만 컴퓨터가 마우스와 스피커만으로 만들어지지는 않기 때문에 더 많은 부품이 필요하다. 위처럼 팩토리 메서드 패턴을 사용해서 한다면 모니터Factory클래스, 키보드Factory클래스, 프린터Factory클래스를 정의해야하며, ComputerFactory에서는 너무 많은 객체를 생성하게 된다.
///////////////////////Monitor/////////////////////// //Monitor 인터페이스 public interface Monitor{} //CompanyA에서 만드는 Mouse를 만드는 클래스 정의 public class MonitorA implements Monitor { public MonitorA() { System.out.println("MonitorA 생성"); } } //CompanyB에서 만드는 Monitor를 만드는 클래스 정의 public class MonitorB implements Monitor { public MonitorB() { System.out.println("MonitorB 생성"); } //Monitor 만드는 Factory클래스 정의 public class MonitorFactory { public Monitor createMonitor (String companyName) { //Monitor 객체 생성 Monitor monitor = null; //Company에 따른 조건문 생성 swich (companyName) { //CompanyA case : "CompanyA" : monitor = new MonitorA(); break; //CompanyB case : "CompanyB" : monitor = new MonitorB(); break; } //Monitor 반환 return monitor; } } ///////////////////////Keyboard/////////////////////// //Keyboard 인터페이스 public interface Keyboard{} //CompanyA에서 만드는 Keyboard를 만드는 클래스 정의 public class KeyboardA implements Keyboard { public KeyboardA() { System.out.println("KeyboardA 생성"); } } //CompanyB에서 만드는 Keyboard를 만드는 클래스 정의 public class KeyboardB implements Keyboard { public KeyboardB() { System.out.println("KeyboardB 생성"); } //Keyboard 만드는 Factory클래스 정의 public class KeyboardFactory { public Keyboard createKeyboard (String companyName) { //Speaker 객체 생성 Keyboard keyboard = null; //Company에 따른 조건문 생성 swich (companyName) { //CompanyA case : "CompanyA" : keyboard = new KeyboardA(); break; //CompanyB case : "CompanyB" : keyboard = new KeyboardB(); break; } //Speaker 반환 return keyboard; } } ///////////////////////Printer/////////////////////// //Printer 인터페이스 public interface Printer{} //CompanyA에서 만드는 Printer를 만드는 클래스 정의 public class PrinterA implements Printer { public MouseA() { System.out.println("PrinterA 생성"); } } //CompanyB에서 만드는 Printer 만드는 클래스 정의 public class PrinterB implements Printer { public PrinterB() { System.out.println("PrinterB 생성"); } //Printer를 만드는 Factory클래스 정의 public class Printer Factory { public Printer createSpeaker (String companyName) { //Printer 객체 생성 Printer printer = null; //Company에 따른 조건문 생성 swich (companyName) { //CompanyA case : "CompanyA" : printer = new PrinterA(); break; //CompanyB case : "CompanyB" : printer = new PrinterB(); break; } //Printer 반환 return printer; } } //ComputerFactort 클래스 정의 public class ComputerFactory { public void createComputer(String companyName) { //Mouse와 Speaker를 만드는 클래스 객체 생성(Factory클래스 객체 생성) MouseFactory mousefactory = new MouseFactory(); SpeakerFactory speakerfactory = new SpeakerFactory(); //Monitor, Keyboard, Printer를 만드는 클래스 객체 생성(Factory클래스 객체 생성) MonitorFactory monitorfactory = new MonitorFactory(); KeyboardFactory keyboardfactory = new KeyboardFactory(); PrinterFactory printerfactory = new PrinterFactory(); //Mouse와 Speaker를 만드는 메서드 실행 mousefactory.createMouse(companyName); speakerfactory.createSpeaker(companyName); ///Monitor, Keyboard, Printer를 만드는 메서드 실행 monitorfactory.createMonitor(companyName); keyboardfactory.createKeyboard(companyName); printerfactory.createprinter(companyName); System.out.println("====Computer" + companyName + " 완성===="); } }
물론 이처럼 구현 한다면(팩토리 메서드 패턴을 이용하여), CompanyA에서 만드는 Computer의 부품은 모두 A라는 라벨이 붙을 것이고, CompanyB에서 만드는 Computer의 부품은 B라는 라벨이 붙을 것이다. 하지만 여기서 부품이 무한히 늘어난다고 생각해보자. 그러면 팩토리 클래스를 정의하는 부분은 더욱 길어지고, 이에 따른 팩토리 객체를 생성하는 부분 또한 무한이 늘어날것이다. 이를 보완하기 위한 것이 추상 팩토리 메서드 패턴이다.
1) CompanyAComputerFactory와 CompanyBComputerFactory를 정의한뒤, 이들을 캡슐화하는 ComputerFactory 인터페이스를 정의한다. 그리고 CompanyA에서는 MouseA와 SpeakerA를 생성하므로 각각의 객체를 생성한다.
//ComputerFactory 인터페이스 정의 public interface ComputerFactory { //Mouse와 Speaker를 만드는 추상 메서드 정의 public Mouse createMouse(); public Speaker createSpeaker(); } //CompanyAComputerFactory 클래스 정의 public class CompanyAComputerFactory implements ComputerFactory { //MouseA객체를 만들어 반환하는 메서드 생성 public MouseA createMouse() { return new MouseA(); } //SpeakerA객체를 만들어 반환하는 메서드 생성 public SpeakerA createSpeaker() { return new SpeakerA(); } } //CompanyBComputerFactory 클래스 정의 public class CompanyBComputerFactory implements ComputerFactory { //MouseB객체를 만들어 반환하는 메서드 생성 public MouseB createMouse() { return new MouseB(); } //SpeakerB객체를 만들어 반환하는 메서드 생성 public SpeakerB createSpeaker() { return new SpeakerB(); } }
2) ComputerFactory2 클래스 정의
ComputerFactory2 클래스는 팩토리 메서드 패턴의 ComputerFactory와 같은 역할을 하는 클래스이다.
//ComputerFactory2 클래스 정의 public class ComputerFactory2 { //companyName에 따른 Computer를 만드는 메서드 생성 public void createComputer(String companyName) { //인터페이스 ComputerFactory 객체 생성 ComputerFactory computerFactory = null; //companyName에 따른 조건문 생성 swich (companyName) { //CompanyA case : "CompanyA" : companyFactory = new CompanyFactory(); break; //CompanyB case : "CompanyB" : companyFactory = new CompanyFactory(); break; } //Mouse와 Speaker를 만드는 매서드 실행 computerFactory.createMouse(); computerFactory.createSpeaker(); } }
3) Computer를 만들 Company클래스 생성(main 클래스)
public class Company { public static void main(String args[]) { //ComputerFactory2 클래스 객체 생성 ComputerFactory2 computerFactory2 = new ComputerFactory2(); //Computer를 만드는 메서드 실행 computerFactory2.createComputer("A"); System.out.println("===구분선===") computerFactory2.createComputer("B"); } } //결과값 MouseA 생성 SpeakerA 생성 ===ComputerA 완성=== ===구분선=== MouseB 생성 SpeakerB 생성 ===ComputerB 완성===
장점
- 구체적은 클래스를 분리한다. 응용프로그램(인터페이스)이 생성할 객체의 클래스를 제어할 수 있다. 그렇기 때문에 구체적인 구현 클래스가 사용자에게 분리되므로, 일반 프로그램은 추상 인터페이스를 통해서만 인스턴스를 조작한다.
- 제품군(Mouse, Speaker)을 쉽게 대체할 수 있다.
단점
1, 새로운 종류의 제품을 제공하기 어렵다. 새롭게 생성되는 제품은 추상 팩토리가 생성할 수 있는 제품 집합에만 고정되어 있기때문에 새로운 종류의 제품을 추가한다면, 팩토리의 구현을 변경해야한다.(추상 팩토리와 모든 서브 클래스를 변경해야 한다.)