본문으로 건너뛰기

디자인 패턴: 팩토리 패턴 (Factory Pattern)

무료2015-03-06#Design_Pattern#工厂模式#工厂方法模式#抽象工厂模式#Factory Pattern

예전에는 XXXFactory 클래스를 정의하여 new 객체를 생성하는 것이 팩토리 패턴이라고 생각했습니다. 게다가 편의를 위해 팩토리 클래스의 Create 메서드를 static 메서드로 정의하는 것이 일반적이었습니다. 하지만 자세히 공부한 후 팩토리 패턴은 그렇게 단순한 것이 아니며, 엄밀히 말하면 '심플 팩토리 패턴'이라고 불리는 이 방식은 '패턴'으로 볼 수 없다는 것을 알게 되었습니다.

no_mkd

일.팩토리 패턴이란?

1.'심플 팩토리 패턴', Simple Factory Pattern

즉, Factory 클래스에서 객체를 new 하는 정적 팩토리 메서드를 정의하는 일반적인 방식입니다. 요약에서 '엄밀히 말하면, 심플 팩토리 패턴이라고 불리는 이 방식은 패턴으로 볼 수 없다'고 언급했지만, 정적 팩토리 메서드는 진정한 '디자인 패턴'은 아니지만, 이 방식의 응용도 널리 사용되며 몇 가지 이점을 가져다주므로, '디자인 패턴'이 아니라고 해서 버려서는 안 됩니다.

2.팩토리 메서드 패턴, Factory Method Pattern

팩토리 패턴의 핵심입니다. 추상적인 '팩토리 메서드'를 정의하여 객체를 new 하고, 해당 클래스의 확장 클래스가 new 의 과정을 구현합니다.

이 방식은 객체 생성 과정과 객체 행위를 분리합니다 (자세한 설명은 생략합니다). 정확히 말하면, 객체 생성을 '하향'합니다 (여기서의 동사에 주의——'하향').

3.추상 팩토리 패턴, Abstract Factory Pattern

팩토리 메서드 패턴의 응용 및 확장입니다. 여러 '팩토리 메서드'를 추상 팩토리 클래스에 캡슐화하여 '한 세트의객체를 new 하는'목적을 달성합니다.

이.일반적인 방식 (팩토리 패턴 사용 안 함)

옷을 파는 작은 가게를 연다고 가정해 봅시다. 먼저, ColthesStore 클래스가 주문을 받아 완성된 옷을 반환해야 합니다. ClothesStore 클래스는 Clothes 클래스에 의존해야 합니다. 결국 조작할 구체적인 객체는 Clothes 이기 때문입니다. ClothesStore 에는 옷을 만드는 공정 (제작, 장식 가공 등) 도 필요합니다. 그렇다면 우리의 ClothesStore 는 다음과 같을 것입니다:

package FactoryPattern;

/**

  • @author ayqy
  • 不使用工厂模式,直接创建具体对象

*/ public class ClothesStore {

String type;//定义衣服类型
Clothes clothes;//衣服对象

/**
 * @author ayqy
 * 定义内部类 Clothes
 *
 */
class Clothes{
	String type;//类型
	String desc;//描述
	String cloth;//布料
	
	public Clothes(String type, String desc, String cloth){
		this.type = type;
		this.desc = desc;
		this.cloth = cloth;
	}
	
	public String toString(){
		return type+ "," + desc + "," + cloth;
	}
}

public ClothesStore(String type){
	this.type = type;
}

/**
 * @return 返回未经装饰加工的衣服
 */
private Clothes createClothes(String type){
	if(type.equals("外套")){
		return new Clothes("外套", "一件好看的外套", "麻布");
	}else if(type.equals("内衣")){
		return new Clothes("内衣", "一件舒适的内衣", "棉布");
	}else
		return null;
}

/**
 * @return 返回做好的衣服
 */
public Clothes getClothes(){
	clothes = createClothes(type);
	decorate(clothes);//对衣服进行装饰
	
	return clothes;
}

private void decorate(Clothes clothes){
	//给衣服添加装饰(图案、纹样等等)
	System.out.println("精心装饰完毕,衣服变得更漂亮了");
}

}

P.S. 코드에서 Clothes 를 내부 클래스로 정의한 것은 단순히 분량을 절약하고 구조를 간소화하기 위한 것이며, 효과는 동일합니다.

ClothesStore 가 Clothes 에 매우 강한 의존성을 가지고 있음 ('구체'에 과도하게 의존하는 것은 좋은 일이 아닙니다..) 것을 알 수 있습니다. Clothes 가 변경되면 (예: 새로운 멤버 변수와 멤버 함수가 추가된 경우), ClothesStore 는 그에 따라 수정해야 할 수 있습니다. 특히 옷 객체를 new 하는 부분:

/**
* @return 返回未经装饰加工的衣服
*/
private Clothes createClothes(String type){
if(type.equals("外套")){
		return new Clothes("外套", "一件好看的外套", "麻布");
	}else if(type.equals("内衣")){
		return new Clothes("内衣", "一件舒适的内衣", "棉布");
	}else
		return null;
}

새로운 type 이 생기면 else if 를 추가하여 변화에 대응해야 합니다. 게다가 ClothesStore 가 구체적인 Clothes 클래스와 바인딩되어 있어 ClothesStore 는 확장이 어렵고 재사용이 어렵습니다.

삼.'심플 팩토리 패턴'

createClothes 메서드를 ClothesFactory 로 이동하여 정적 팩토리 메서드로 정의합니다:

package FactoryPattern.SimpleFactoryPattern;

/**

  • @author ayqy
  • 定义静态工厂方法

/ public class ClothesFactory { /* * @return 返回未经装饰加工的衣服 */ public static Clothes createClothes(String type){ if(type.equals("外套")){ return new Clothes("外套", "一件好看的外套", "麻布"); }else if(type.equals("内衣")){ return new Clothes("内衣", "一件舒适的内衣", "棉布"); }else return null; } }

P.S. 이번에는 Clothes 를 독립적인 클래스로 분리해야 합니다 (내부 클래스는 접근할 수 없음). 그 외에도 ClothesStore 의 getClothes 메서드도 그에 따라 변경해야 합니다..이러한 세부 사항은 신경 쓰지 마십시오.

심플 팩토리 패턴을 적용한 후, new 객체 부분을 독립시킬 수 있었습니다. 이 장점은:

Clothes 가 변경될 때, ClothesStore 의 긴 코드에서 Create 부분을 찾는 것이 아니라 ClothesFactory 에서 직접 create 메서드를 수정할 수 있습니다. 게다가 이전의 '일반적인 방식'과 비교하여 코드가 거의 변경되지 않았으므로, 이전 코드를 심플 팩토리 패턴을 적용한 코드로 쉽게 변경할 수 있습니다. 따라서 많은 개발자가 이 방식을 선호합니다.

'심플 팩토리 패턴'은 진정한 팩토리 패턴이 아니며, 그 이점도 매우 제한적이지만, 괜찮습니다. 팩토리 메서드 패턴이 있습니다. 이는 진짜 팩토리 패턴입니다.

사.팩토리 메서드 패턴

추상적인 '팩토리 메서드'를 정의하여 구체적인 구현을 담당합니다.全新的인 ClothesStore 는 다음과 같을 것입니다:

package FactoryPattern.FactoryMethodPattern;

/**

  • @author ayqy
  • 使用工厂方法模式,定义抽象的工厂方法来 new 产品

*/ public abstract class ClothesStore {

String type;//定义衣服类型
Clothes clothes;//衣服对象

public ClothesStore(String type){
	this.type = type;
}

//定义工厂方法,由扩展类来实现具体制作细节
public abstract Clothes createClothes(String type);

/**
 * @return 返回做好的衣服
 */
public Clothes getClothes(){
	clothes = createClothes(type);
	decorate(clothes);//对衣服进行装饰
	
	return clothes;
}

private void decorate(Clothes clothes){
	//给衣服添加装饰(图案、纹样等等)
	System.out.println("精心装饰完毕,衣服变得更漂亮了");
}

}

ClothesStore 가 Abstract 가 된 것을 알 수 있습니다. 옷의 제작 공정과 구현 세부 사항이 캡슐화되어 있지만, Create 과정은 구현되지 않았습니다.

다음으로 ClothesStore 클래스를 확장하여 구체적인 Create 세부 사항을 구현합니다:

package FactoryPattern.FactoryMethodPattern;

/**

  • @author ayqy
  • 扩展 ClothesStore,实现具体制作细节

*/ public class DefaultClothesStore extends ClothesStore{

public DefaultClothesStore(String type) {
	super(type);
}

/* 
 * 实现具体制作细节
 */
 @Override
public Clothes createClothes(String type) {
	if(type.equals("外套")){
		return new Clothes("外套", "一件好看的外套", "麻布");
	}else if(type.equals("内衣")){
		return new Clothes("内衣", "一件舒适��内衣", "棉布");
	}else
		return null;
}

}

네, 이제 팩토리 메서드 패턴을 성공적으로 적용했습니다. 이전의 심플 팩토리 패턴과 비교하여, 이 진짜 팩토리 패턴에는 어떤 특징이 있는지 살펴보겠습니다.

  1. 마찬가지로 new 객체 과정과 객체 행위의 분리를 구현했지만, 다른 점은 상속 (확장) 을 사용하여 구현했다는 것입니다. 즉, 서두에서 언급한 '하향'(구체적인 구현을 더 낮은 클래스 레벨에 배치) 입니다.
  2. 각 구체적인 Store 는 고유한 Create 세부 사항을 구현해야 하지만, 동시에 베이스 클래스 Store 의 제작 공정 (decorate 메서드 등) 을 활용할 수 있습니다.
  3. 추상적인 팩토리 메서드 하나로 팩토리 패턴을 쉽게 구현했습니다 (Factory 는 전혀 나타나지 않았지만, 확실히 '팩토리'를 구현했습니다,不是吗?).

오.추상 팩토리 패턴

추상 팩토리 패턴은 이전 방식의 확장입니다. 위에서는 하나의 제품만 Create 할 수 있었지만, 많은 경우 한 세트의 제품을 Create해야 합니다. 예를 들어, 옷의 원료에는 천, 염료, 실, 단추 등이 있습니다. 이때 추상 팩토리 패턴을 적용하여 구현합니다:

package FactoryPattern.AbstractFactoryPattern;

/**

  • @author ayqy
  • 定义原料工厂

*/ public abstract class ResourcesFactory { public abstract Cloth getCloth();//获取布料 public abstract Color getColor();//获取染料 public abstract Button getButton();//获取纽扣 //需要的其它工厂方法 }

추상 팩토리 내의 타입은 모두 사용자 정의 인터페이스입니다. 예를 들어:

package FactoryPattern.AbstractFactoryPattern;

/**

  • @author ayqy
  • 定义 Cloth 接口 */ public interface Cloth { //属性 //行为 }

원료 팩토리가 있으므로, ClothesStore 도 이 새로운 구조에 적응하기 위해 그에 따라 변경해야 합니다:

package FactoryPattern.AbstractFactoryPattern;

/**

  • @author ayqy
  • 扩展 ClothesStore,实现具体制作细节

*/ public class DefaultClothesStore extends ClothesStore{

public DefaultClothesStore(String type, ResourcesFactory res) {
	super(type, res);
}

/* 
 * 实现具体制作细节
 */
 @Override
public Clothes createClothes(String type) {
	if(type.equals("外套")){
		//获取所需原料
		Cloth cloth = res.getCloth();
		Color color = res.getColor();
		Button button = res.getButton();
		//制作衣服
		return new Clothes("外套", color.toString() + button.toString(), cloth.toString());
	}else if(type.equals("内衣")){
		//获取所需原料
		Cloth cloth = res.getCloth();
		Color color = res.getColor();
		Button button = res.getButton();
		//制作衣服
		return new Clothes("内衣", color.toString() + button.toString(), cloth.toString());
	}else
		return null;
}

}

Store 객체를 생성할 때 구체적인 ResourcesFactory 매개변수가 필요합니다 (구체적인 원료 팩토리가 모든 getXXX 팩토리 메서드를 구현함). 구체적인 Store 는 구체적인 제작 과정을 담당하며, 필요한 원료는 ResourceFactory 에서 얻을 수 있습니다. 구체적인 Store 가 옷을 만든 후, Store 베이스 클래스가 공정 가공 (decorate 메서드 등) 을 담당하고, 마지막으로 구체적인 Store 가 정가공된 옷을 반환합니다. 전체 구조는 느슨하고, 낮은 결합도, 확장이 용이합니다.

그 외에도 OO 설계 원칙——'의존성 역전 원칙'을 언급할 필요가 있습니다. 클래스 구조를 관찰하면 알 수 있습니다:

상위 컴포넌트 Store 는 Clothes 에 의존하고, 동시에 하위 컴포넌트 ResourcesFactory 도 Clothes 에 의존합니다 (이것이 '의존성 역전'입니다. 즉, 하위 컴포넌트가 반대로 상위 컴포넌트에 의존하고, 동시에 상위와 하위 컴포넌트가 모두 추상에 의존함). 이러한 설계는 확장이 용이하다는 장점이 있습니다 (추상은 확장 가능함을 의미합니다..).

육.요약

실제로 첫 번째 부분이 이미 요약입니다. 여기서 강조하고 싶은 것은 사물에는 양면성이 있으며, 좋은 것에는 반드시 단점이 존재한다는 것입니다. 디자인 패턴도 예외는 아닙니다.

추상 팩토리 패턴은 정말 훌륭해 보이지만, 치명적인 단점도 존재합니다: 다중 추상으로 인한 구조의 복잡화 문제

댓글

아직 댓글이 없습니다

댓글 작성