theory/디자인패턴

GoF의 디자인 패턴. 생성패턴 1.추상 팩토리

아르르르를를르 2021. 8. 8. 17:59

생성패턴(creational pattern)

인스턴스를 만드는 절차를 추상화하는 패턴

특징 1) 생성패턴은 시스템이 어떤 구체 클래스를 사용하는지에 대한 정보를 캡슐화한다.

특징 2) 생성패턴은 이들 클래스의 인스턴스들이 어떻게 만들고 어떻게 서로 맞붙는지에 대한 부분을 완전히 가려준다. 

예시) java toString() 메소드처럼 목적은 같지만 생성한 클래스에 따라 결과물이 다르게 생성되는 메소드가 있다.

-> 결론적으로 생성에 대한 유연성을 확보할 수 있다.

 

- 생성패턴은 5개이고, 추상 팩토리부터 다뤄보도록 한다.

추상 팩토리 (Abstract Factory)

빌더 (Builder)

팩토리 메서드 (Factory Methods)

프로토타입 (Prototype)

싱글턴 (Singleton)

 


추상 팩토리 (Abstract Factory)

- 의도

상세화된 서브클래스를 정의하지 않고도 서로 관련성 있거나 독립적인 여러 객체의 군을 생성하기 위한 인터페이스 제공. 

한마디로 추상화된 인터페이스를 불러와서 구체화된 객체를 생성한다는 것이다.

 

- 동기

서로 관련있는 팩토리 클래스들을 캡슐화하여 사용자는 캡슐화된 상위 클래스만 조건에 따라 생성한다.

 

- 활용성

1. 객체가 생성되거나 구성, 표현되는 방식과 무관하게 시스템을 독립적으로 만들고자 할 때

2. 여러 제품군 중 하나를 선택해서 시스템을 설정해야 하고 한번 구성한 제품을 다른 것으로 대체할 수 있을 때

3. 관련 제품 객체들이 함께 사용되도록 설계되었고, 이 부분에 대한 제약이 외부에도 지켜지도록 하고 싶을 때

4. 제품에 대한 클래스 라이브러리를 제공하고, 그들의 구현이 아닌 인터페이스를 노출시키고 싶을 때

 

- 구조

 

- 참여자

  • AbstractFactory: 개념적 제품에 대한 객체를 생성하는 연산으로 인터페이스를 정의한다.
  • ConcreteFactory: 구체적인 제품에 대한 객체를 생성하는 연산을 구현한다.
  • AbstractProduct: 개념적 제품 객체에 대한 인터페이스를 정의한다.
  • ConcreteProduct: 구체적으로 팩토리가 생성할 객체를 정의하고, AbstractProduct가 정의하는 인터페이스를 구현한다.
  • Client: AbstractFactory, AbstractProduct 에 선언된 인터페이스를 사용하는 사용자다.

 

- 결과

  1. 구체적인 클래스를 사용자에게서 분리한다.
  2. 제품군을 쉽게 대체할 수 있도록 한다.
    응용프로그램에서 보통 AbstractFactory를 한번만 생성하기 때문이다.
  3. 제품 사이 일관성 증진시킨다.
    응용프로그램이 한번에 오직 한군에서 만든 객체를 사용하도록 함으로써 프로그램에 일관성을 보장한다.
  4. 새로운 종류의 제품을 제공하기 어렵다.
    생성되는 제품이 추상 팩토리가 생성할 수 있는 ConcreteFactory에 제한되어 있기에 새로운 종류의 제품을 만들려면 팩토리의 구현을 변경해야 한다.
    -> 모든 ConcreteFactory의 변경이 불가피하다.

 

- 구현

  1. Factory를 단일체로 정의
  2. Product을 생성
  3. 확장 가능한 Factory 정의

 

- 예시

super class

public abstract class Computer {
     
    public abstract String getRAM();
    public abstract String getHDD();
    public abstract String getCPU();
     
    @Override
    public String toString(){
        return "RAM= "+this.getRAM()+", HDD="+this.getHDD()+", CPU="+this.getCPU();
    }
}

 

sub class (1)

public class PC extends Computer {
 
    private String ram;
    private String hdd;
    private String cpu;
     
    public PC(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    @Override
    public String getRAM() {
        return this.ram;
    }
 
    @Override
    public String getHDD() {
        return this.hdd;
    }
 
    @Override
    public String getCPU() {
        return this.cpu;
    }
 
}

 

sub class (2)

public class Server extends Computer {
 
    private String ram;
    private String hdd;
    private String cpu;
     
    public Server(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    @Override
    public String getRAM() {
        return this.ram;
    }
 
    @Override
    public String getHDD() {
        return this.hdd;
    }
 
    @Override
    public String getCPU() {
        return this.cpu;
    }
 
}

여기까지는 뒤에 나올 팩토리 패턴과 같으나 뒷부분에서 차이가 발생한다.

추상 팩토리의 역할을 하는 인터페이스 또는 추상 클래스가 필요하다.

public interface ComputerAbstractFactory {
 
	public Computer createComputer();
 
}

앞으로 팩토리 인터페이스를 구현하는 클래스에서 createComputer() 메소드를 오버라이딩하며 각각의 서브 클래스를 리턴해줄 것이다.

 

PCFactory

public class PCFactory implements ComputerAbstractFactory {
 
	private String ram;
	private String hdd;
	private String cpu;
	
	public PCFactory(String ram, String hdd, String cpu){
		this.ram=ram;
		this.hdd=hdd;
		this.cpu=cpu;
	}
	@Override
	public Computer createComputer() {
		return new PC(ram,hdd,cpu);
	}
 
}

ServerFactory

public class ServerFactory implements ComputerAbstractFactory {
 
	private String ram;
	private String hdd;
	private String cpu;
	
	public ServerFactory(String ram, String hdd, String cpu){
		this.ram=ram;
		this.hdd=hdd;
		this.cpu=cpu;
	}
	
	@Override
	public Computer createComputer() {
		return new Server(ram,hdd,cpu);
	}
 
}

이렇게 sub class에 대한 factory class가 구현되었다.

마지막으로 이 sub class들을 생성하기 위해 consumer class를 만들어보도록 한다.

 

ComputerFactory

public class ComputerFactory {
 
	public static Computer getComputer(ComputerAbstractFactory factory){
		return factory.createComputer();
	}
}

 

이제 클라이언트는 이 ComputerFacotry 클래스의 getComputer()라는 static 메소드에 앞서 구현한 PCFactory나 ServerFactory 인스턴스를 넣어줌으로써 if-else 없이도 각각 원하는 서브 클래스의 인스턴스를 생성할 수 있게 된다.

public class AbstractFactoryTest {
 
	public static void main(String[] args) {
		Computer pc = ComputerFactory.getComputer(new PCFactory("2 GB","500 GB","2.4 GHz"));
		Computer server = ComputerFactory.getComputer(new ServerFactory("16 GB","1 TB","2.9 GHz"));
		System.out.println("AbstractFactory PC Config::"+pc);
		System.out.println("AbstractFactory Server Config::"+server);
	}
    
}

 

Output

AbstractFactory PC Config::RAM= 2 GB, HDD=500 GB, CPU=2.4 GHz
AbstractFactory Server Config::RAM= 16 GB, HDD=1 TB, CPU=2.9 GHz

 

- 관련 패턴

AbstractFactory 는 뒤에 나올 원형 패턴을 이용할 때도 있다.

ConcreteFactory 는 단일채 패턴을 이용해 구현하는 경우가 많다.

 

 

- 참고문헌

  • GoF의 디자인 패턴(개정판) / 에릭 감마, 리처드 헬름, 랄프 존슨, 존 블라시디스 공저 / 김정아 역 / 프로텍미디어 / 발행 2015년 03월 26일
  • 예시: https://readystory.tistory.com/119