반응형

팩토리 패턴(Factory Pattern)

 

팩토리 패턴은 Creational Patterns 종류 중 하나이다.

 

new가 나오는 연산자는 특정 Concrete Class에게 의존성을 가지게 된다.

따라서 생성 패턴은 직접적인 new operator을 쓰지 않고도 객체를 얻어 내도록 하는 것이다.

ex) Factory, Singleton, builder, Prototype

 

Factory Method = class scope -> 상속

Abstract Factory = object scope -> 객체 생성 위임

 

즉, 팩토리 패턴의 핵심은 클래스의 인스턴스를 만드는 것을 서브클래스에서 결정하도록 한다는 것이다. 즉, new 키워드를 사용하는 부분을 서브클래스에 위임함으로서 객체 생성을 캡슐화하고 구상 클래스에 대한 의존성이 줄어든다는 이점을 얻을 수 있다.

 

 

 

사용되는 디자인 패턴 원칙

DIP (Dependency Inversion Principle)

 

의존관계 역전원칙은 특정 형태의 소프트웨어 모듈을 분리(Decoupling) 하는 것을 의미한다.

객체 사이에 서로 도움을 주고받으면 의존관계가 형성된다.

 

DIP는 무분별한 의존 관계를 맺는 것을 방지하기 위해 의존 관계를 맺을 시 지켜야할 기본적인 형식을 의미한다.

DIP는 상위 계층이 하위 계층에 의존하는 전통적인 의존 관계를 반전시켜 상위계층이 하위 계층의 구현으로 독립되게 할 수 있다.

 

즉, Concrete class는 얼마든지 변할 수 있기 때문에 abstraction에 대해 dependency를 가지도록 한다.

-> High level component에서 low level component로 dependency를 가지도록 하지 말고 둘다 abstractions에 dependecy를 가지도록 한다.

 

 

 

 

팩토리 메서드 패턴

 

위에서 언급했듯이, 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정하도록 하는 것이다. 팩토리 메서드 패턴은 팩토리 메서드는 객체를 생성해서 반환하는 것을 말한다. 즉, 결과값

이 객체인 것이다.

 

이점
클래스의 변경사항이 생겼을 때 얼마나 다른 클래스에게도 영향을 줄 것인가가 결합도이다. 

팩토리 메소드 패턴은 직접 사용하는 객체를 생성하지 않고 팩토리 메소드 클래스를 통해 객체를 대신 생성하고 그 객체를 반환 받아 사용하기 때문에 효율적인 코드 제어를 할 수 있을 뿐더러 결합도를 낮춰 유지보수가 용이하다.

 

 

팩토리 메서드 패턴 클래스 다이어그램

 

 

 

 

상위 클래스가 객체를 Create하지 않고 선언만 해둔 채, 그것을 subclass가 실제 그 역할을 하도록 한다.

그리고서 모든 하위 클래스들이 공유/재사용 할 수 있는 operation을 제공하는데 이 operation에서는 factoryMethod()를 이용한 구성이 되어있다.

 

factoryMethod에서 어떤 product가 리턴된지 보면 그 product는 anOperation의 서비스를 받을 수 있게된다.

하지만 그런 product는 미리 정의가 불가능하기에 concreteCreator에게 그 역할을 맡기는 것이다.

 

즉, concreteCreator은 factoryMethod를 구현함으로써 상위 클래스의 oepration을 사용할 수 있게 되는 것이고

concreteProduct는 다시 상위 Creator가 알고 있는 product이기 때문에 anOperation 메서드는 concrete한 product를 더 상위의 개념으로 사용 가능하다.

 

 

 

 

팩토리 메서드 패턴 예제

 

Milk 추상 클래스

getName에 대한 내용만 정의해두었다.

package FactoryMethodPattern;

public abstract class Milk {
	public abstract String getName();
}

 

각 milk에 대한 클래스

Milk를 상속받아 직접 구현해둔다.

package FactoryMethodPattern;

public class ChocoMilk extends Milk{
	@Override
	public String getName() {
		return "ChocoMilk";
	}
}
package FactoryMethodPattern;

public class StrawberryMilk extends Milk{
	@Override
	public String getName() {
		return "StrawberryMilk";
	}
}
package FactoryMethodPattern;

public class WhiteMilk extends Milk{
	@Override
	public String getName() {
		return "WhiteMilk";
	}
}

 

MilkFactory 추상 클래스

package FactoryMethodPattern;

public abstract class MilkFactory {
	public abstract Milk createMilk(String type);
}

 

TypeMilkFactory 클래스

각 Milk에 관해 객체를 생성하고 리턴해준다.

package FactoryMethodPattern;

public class TypeMilkFactory extends MilkFactory{

	@Override
	public Milk createMilk(String type) {
		Milk milk;
		
		switch (type) {
			case "Choco":
				milk = new ChocoMilk();
				break;
			case "Strawberry":
				milk = new StrawberryMilk();
				break;
			default:
				milk = new WhiteMilk();
		}
		return milk;
	}
}

 

 

FactoryMethodMain 클래스

package FactoryMethodPattern;

public class FactoryMethodMain {
	public static void main(String[] args) {
		TypeMilkFactory typeMilkFactory = new TypeMilkFactory();

		Milk StrawberryMilk = typeMilkFactory.createMilk("Strawberry");
		Milk ChocoMilk = typeMilkFactory.createMilk("Choco");
		Milk WhiteMilk = typeMilkFactory.createMilk("");

		System.out.println(StrawberryMilk.getName());
		System.out.println(ChocoMilk.getName());
		System.out.println(WhiteMilk.getName());		
	}
}

 

StrawberryMilk 
ChocoMilk 
WhiteMilk 

 

인스턴스 생성을 서브 클래스로 위임한 결과이다. 

최종 메인 메서드에서는 new 키워드를 사용하여 인스턴스를 생성한 부분이 없는 것을 확인할 수 있다. 

 

이를 통해 메인 프로그램에서는 어떤 객체가 생성되었는지 신경 쓸 필요 없이 반환된 객체만 사용하면 되고 Milk 클래스에서 변경이 발생해도 메인 프로그램이 변경되는 것은 최소화할 수 있다.

 

 

 

Factory Method Pattern


Abstract Factory Pattern

 

추상 팩토리 패턴

팩토리 패턴보다 더 Abstraction한 패턴이다.

만들고자 하는 product가 하나가 아닌 다수 일 때, responsibility가 하위 클래스가 아닌 다른 object일 때 사용한다.

 

추상 팩토리 패턴은 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있다. 

즉, 연관된 서브 클래스를 그룹화할 수 있고 이것은 이 그룹을 자유롭게 교체할 수 있는 패턴이라고 할 수 있다.

 

 

 

추상 팩토리 클래스 다이어 그램

 

 

 

 

추상 팩토리 패턴 예제

 

Milk에 대한 추상 클래스 및 이를 구현한 ChocoMilk, StrawberryMilk 클래스이다.

package AbstractFactoryPattern;

public abstract class Milk {
	public abstract String getName();
}

package AbstractFactoryPattern;

public class ChocoMilk extends Milk{
	@Override
	public String getName() {
		return "ChocoMilk";
	}
}
package AbstractFactoryPattern;

public class StrawberryMilk extends Milk{
	@Override
	public String getName() {
		return "StrawberryMilk";
	}
}

 

Milk 추상 팩토리 인터페이스와 ChocoFactory, StrawberryFactory. 

이때 각 팩토리에서 new를 통해 객체를 생성해준다.

package AbstractFactoryPattern;

public interface MilkAbstractFactory {
	public Milk CreateMilk();
}
package AbstractFactoryPattern;

public class ChocoFactory implements MilkAbstractFactory{
	@Override
	public Milk CreateMilk() {
		return new ChocoMilk();
	}	
}
package AbstractFactoryPattern;

public class StrawberryFactory implements MilkAbstractFactory{
	@Override
	public Milk CreateMilk() {
		return new StrawberryMilk();
	}	
}

 

MilkFactory 클래스

해당하는 클래스의 CreateMilk를 통해 객체를 생성해준다.

package AbstractFactoryPattern;

public class MilkFactory {
	public static Milk getMilk(MilkAbstractFactory milkAbstractFactory) {
		return milkAbstractFactory.CreateMilk();
	}
}

 

AbstractFactoryMain 클래스

추상 팩토리 클래스 구현을 볼 수 있다.

package AbstractFactoryPattern;

public class AbstractFactoryMain {

	public static void main(String[] args) {
		Milk chocoMilk = MilkFactory.getMilk(new ChocoFactory());
		Milk strawberryMilk = MilkFactory.getMilk(new StrawberryFactory());

		System.out.println(chocoMilk.getName());
		System.out.println(strawberryMilk.getName());
	}

}
ChocoMilk
StrawberryMilk

 

위 결과 코드와 같이 Milk별 팩토리 클래스를 파라미터로 넘겨 각 슈트 객체를 반환 받아 사용할 수 있다. 

이렇게 되면 팩토리 클래스 교체만으로 조금 더 유연하게 기능의 수정, 확장에 대처할 수 있게 된다. 

코드 상으로는 if-else 구문을 제거하여 조금 더 깔끔하게 코드를 구성할 수 있다.

 

출처

https://woovictory.github.io/2019/02/07/Design-Pattern-Factory-Pattern/

 

반응형