CS_Step7 - 추상 팩토리 메서드 패턴(Abstract Factory Method Pattern)

장선웅·2022년 7월 19일
0

팩토리 메서드 패턴 VS 추상 팩토리 메서드 패턴

  • 팩토리 메서드 패턴 : 조건에 따른 객체 생성을 팩토리 클래스로 위임하여, 팩토리 클래스에서 객체를 생성하는 패턴
  • 추상 팩토리 메서드 패턴 : 서로 관련있는 객체들을 다 모아 팩토리 클래스를 만들고, 이들 팩토리를 조건에 따라 생성하도록 다시 팩토리를 만들어 객체를 생성하는 패턴

간단히 말하면, 팩토리 메서드 패턴을 좀 더 캡슐화한 패턴이다. 하지만 주의해야 할 것은 추상 메서드 팩토리 패턴이 팩토리 메서드 패턴의 상위호환은 아니며, 상황에 따라 적절히 사용해야한다.

1. 추상 팩토리 메서드 패턴은 왜 사용할까?(feat.팩토리 메서드 패턴)

예를 들어, 회사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라는 라벨이 붙을 것이다. 하지만 여기서 부품이 무한히 늘어난다고 생각해보자. 그러면 팩토리 클래스를 정의하는 부분은 더욱 길어지고, 이에 따른 팩토리 객체를 생성하는 부분 또한 무한이 늘어날것이다. 이를 보완하기 위한 것이 추상 팩토리 메서드 패턴이다.


2. 추상 팩토리 메서드 패턴 구현 방법

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 완성===

3. 추상 팩토리 메서드 패턴의 장/단점

장점

  1. 구체적은 클래스를 분리한다. 응용프로그램(인터페이스)이 생성할 객체의 클래스를 제어할 수 있다. 그렇기 때문에 구체적인 구현 클래스가 사용자에게 분리되므로, 일반 프로그램은 추상 인터페이스를 통해서만 인스턴스를 조작한다.
  2. 제품군(Mouse, Speaker)을 쉽게 대체할 수 있다.

단점

1, 새로운 종류의 제품을 제공하기 어렵다. 새롭게 생성되는 제품은 추상 팩토리가 생성할 수 있는 제품 집합에만 고정되어 있기때문에 새로운 종류의 제품을 추가한다면, 팩토리의 구현을 변경해야한다.(추상 팩토리와 모든 서브 클래스를 변경해야 한다.)

profile
개발을 꿈꾸는 초짜

0개의 댓글