디자인 패턴 : 팩토리 vs 팩토리 메소드 vs 추상 팩토리


183

웹 사이트에서 디자인 패턴을 읽고있었습니다

거기에서 Factory, Factory method 및 Abstract factory에 대해 읽었지만 혼란스럽고 정의에 명확하지 않습니다. 정의에 따르면

팩토리-인스턴스화 로직을 클라이언트에 노출시키지 않고 객체를 작성하고 공통 인터페이스를 통해 새로 작성된 객체를 참조합니다. Factory Method의 단순화 된 버전입니다

팩토리 메소드-오브젝트를 작성하기위한 인터페이스를 정의하지만 서브 클래스가 인스턴스화 할 클래스를 결정하고 공통 인터페이스를 통해 새로 작성된 오브젝트를 참조하도록합니다.

Abstract Factory-클래스를 명시 적으로 지정하지 않고 관련 객체 패밀리를 작성하기위한 인터페이스를 제공합니다.

또한 Abstract Factory와 Factory Method에 관한 다른 stackoverflow 스레드를 보았지만 거기에 그려진 UML 다이어그램은 이해를 훨씬 더 악화시킵니다.

누구든지 제게 말해 줄 수 있습니까

  1. 이 세 가지 패턴은 어떻게 다른가요?
  2. 언제 사용합니까?
  3. 또한 가능한 경우 이러한 패턴과 관련된 Java 예제가 있습니까?

3
OP와 거의 동일한 질문에 대한 답변을 찾고 있었지만이 기사를 찾았습니다 : 공장 없음에서 공장 방법 . 샘플 프로젝트의 진화에 따라 통찰력을 제공합니다 (제목에 언급 된 팩토리 방법은 진화 단계 중 하나임).
Nick Alexeev

답변:


251

세 가지 팩토리 유형 모두 동일한 작업을 수행합니다. "스마트 생성자"입니다.

Apple과 Orange라는 두 가지 종류의 과일을 만들 수 있다고 가정 해 봅시다.

공장

팩토리는 "고정"되었습니다. 서브 클래 싱없이 구현이 하나뿐이기 때문입니다. 이 경우 다음과 같은 클래스가 있습니다.

class FruitFactory {

  public Apple makeApple() {
    // Code for creating an Apple here.
  }

  public Orange makeOrange() {
    // Code for creating an orange here.
  }

}

사용 사례 : Apple 또는 Orange를 생성하는 것은 생성자를 처리하기에 너무 복잡합니다.

공장 방법

팩토리 메소드는 일반적으로 클래스에 일반 처리가 있지만 실제로 사용하는 과일의 종류를 변경하려는 경우에 사용됩니다. 그래서:

abstract class FruitPicker {

  protected abstract Fruit makeFruit();

  public void pickFruit() {
    private final Fruit f = makeFruit(); // The fruit we will work on..
    <bla bla bla>
  }
}

FruitPicker.pickFruit()하위 클래스에서 팩토리 메소드를 구현 하여 공통 기능을 재사용 할 수 있습니다 .

class OrangePicker extends FruitPicker {

  @Override
  protected Fruit makeFruit() {
    return new Orange();
  }
}

추상 공장

Abstract factory는 일반적으로 의존성 주입 / 전략과 같은 것들에 사용되는데, "동일한 종류"가 필요하고 공통적 인 기본 클래스를 갖는 전체 객체 계열을 생성하고자 할 때. 다음은 모호한 과일 관련 예입니다. 여기서 유스 케이스는 실수로 Apple에서 OrangePicker를 사용하지 않도록하려는 것입니다. 우리가 같은 공장에서 과일과 피커를 얻는 한 일치합니다.

interface PlantFactory {

  Plant makePlant();

  Picker makePicker(); 

}

public class AppleFactory implements PlantFactory {
  Plant makePlant() {
    return new Apple();
  }

  Picker makePicker() {
    return new ApplePicker();
  }
}

public class OrangeFactory implements PlantFactory {
  Plant makePlant() {
    return new Orange();
  }

  Picker makePicker() {
    return new OrangePicker();
  }
}

8
+1 이것은 이러한 패턴에 대한 나의 이해와 가장 비슷한 답입니다. 호출 코드 (클라이언트)의 예를 추가하면 도움이 되겠습니까? 나를 많이 귀찮게하는 질문은 다음과 같습니다. Abstract Factory Pattern은 Factory Method Pattern으로 공장 확장 된 것이라고 말할 수 있습니까?
croraf

9
이것은 내가 몇 년 동안 찾은 예입니다.
Taztingo

환상적인 설명입니다! 감사!
André Andrade

@ AndréAndrade 팩토리 메소드 를 호출하는 방법 ? 작은 코드 samle pls :) 사용에 대한 의심이 사라질 것입니다
Arnab Dutta

25
  1. 이 세 가지 패턴은 어떻게 다른가요?

팩토리 : 인스턴스화 로직을 클라이언트에 노출시키지 않고 객체를 만듭니다.

팩토리 메소드 : 객체를 생성하기위한 인터페이스를 정의하지만 서브 클래스가 인스턴스화 할 클래스를 결정하도록합니다. Factory 메소드는 클래스가 서브 클래스에 대한 인스턴스화를 연기하게합니다

Abstract Factory : 구체적인 클래스를 지정하지 않고 관련 또는 종속 객체의 패밀리를 작성하기위한 인터페이스를 제공합니다.

AbstractFactory 패턴은 컴포지션을 사용하여 객체 생성 책임을 다른 클래스에 위임하는 반면 Factory 메소드 디자인 패턴은 상속을 사용하고 파생 클래스 또는 서브 클래스를 사용하여 객체 생성

  1. 언제 사용합니까?

팩토리 : 클라이언트는 클래스 만 필요하며 어떤 구체적인 구현을하고 있는지 상관하지 않습니다.

팩토리 메소드 : 클라이언트는 런타임에 작성해야하는 구체적인 클래스를 알지 못하지만 작업을 수행 할 클래스를 가져 오려고합니다.

AbstactFactory : 시스템이 여러 제품군을 작성해야하거나 구현 세부 사항을 노출하지 않고 제품 라이브러리를 제공하려는 경우.

추상 팩토리 클래스는 종종 팩토리 메소드로 구현됩니다. 팩토리 메소드는 일반적으로 템플리트 메소드 내에서 호출됩니다.

  1. 또한 가능한 경우 이러한 패턴과 관련된 Java 예제가 있습니까?

공장 및 공장 방법

의지:

객체를 생성하기위한 인터페이스를 정의하되 하위 클래스가 인스턴스화 할 클래스를 결정하도록합니다. 팩토리 메소드는 클래스가 인스턴스화를 서브 클래스로 연기하도록합니다.

UML 다이어그램 :

여기에 이미지 설명을 입력하십시오

제품 : Factory 메소드가 작성하는 오브젝트의 인터페이스를 정의합니다.

ConcreteProduct : 제품 인터페이스 구현

제작자 : Factory 메서드 선언

ConcreateCreator : Factory 메서드를 구현하여 ConcreteProduct의 인스턴스를 반환

문제 설명 : 게임 인터페이스를 정의하는 팩토리 메소드를 사용하여 게임 팩토리를 작성하십시오.

코드 스 니펫 :

공장 패턴. 팩토리 메소드는 언제 사용합니까?

다른 창조 패턴과 비교 :

  1. Factory Method를 사용하여 디자인을 시작하고 (더 복잡하고, 사용자 정의가 가능하며, 서브 클래스가 확산 됨) 설계자가 더 많은 유연성이 필요한 곳을 발견함에 따라 Abstract Factory, Prototype 또는 Builder (보다 유연하고 더 복잡한)로 발전

  2. 추상 팩토리 클래스는 종종 팩토리 메소드 로 구현되지만 프로토 타입을 사용하여 구현할 수도 있습니다.

추가 참고 자료 : 소스 제작 디자인 패턴


팩토리 메소드의 경우 슈퍼 클래스를 정의해서는 안됩니까?
Tony Lin

21

Factory- 복잡한 객체를 만들기 위해 별도의 Factory 클래스입니다.

예 : Fruit 객체를 생성하는 FruitFactory 클래스

class FruitFactory{

public static Fruit getFruit(){...}

}

팩토리 메소드 -팩토리에 대한 별도의 전체 클래스 대신 해당 클래스 자체에 하나의 메소드를 팩토리로 추가하십시오.

전의:

Calendar.getInstance() (Java's Calendar)

추상 공장 방법 -공장 공장

예 : 컴퓨터 부품을 위해 공장을 짓고 싶다고합시다. 따라서 랩탑, 데스크탑, 서버와 같은 여러 유형의 컴퓨터가 있습니다.

따라서 각 compter 유형마다 공장이 필요합니다. 그래서 우리는 아래와 같이 하나의 높은 수준의 공장을 만듭니다.

ComputerTypeAbstractFactory.getComputerPartFactory(String computerType) ---> This will return PartFactory which can be one of these ServerPartFactory, LaptopPartFactory, DesktopPartFactory.

이제이 3 가지 자체는 다시 공장입니다. (PartFactory 자체를 다루게 될 것이지만, 추상 팩토리에서 제공 한 것에 따라 별도의 구현이있을 것입니다)

  Interface-> PartFactory. getComputerPart(String s), 
Implementations -> ServerPartFactory, LaptopPartFactory, DesktopPartFactory.

Usage:
new ComputerTypeAbstractFactory().getFactory(“Laptop”).getComputerPart(“RAM”)

편집 : 의견의 반대에 따라 Abstract Factory에 정확한 인터페이스를 제공하도록 편집되었습니다.


1
아이디어 는 컴퓨터 유형별로 인터페이스 ComputerFactory가 아니라 공통 작성 인터페이스 ( getScreen(); getKeyboard(); getDiskdrive(); ...) 가 있다는 것 입니다. 같은 말에서 같은 단어를 두 번 사용하면 디자인 문제가 발생할 수 있습니다 : Laptop Factory.get Laptop Part ().
xtofl

아니요 아니요 아니요, 코드 자체를 정확히 따르지 마십시오. 이해를위한 신학 일뿐입니다. 인터페이스에 대한 정확한 예를 원한다면 여기 있습니다. 개체 : 인터페이스-> ComputerPart, 구현-> RAM, HDD, 프로세서 팩토리 : 인터페이스-> PartFactory. getComputerPart (String s), 구현-> ServerPartFactory, LaptopPartFactory, DesktopPartFactory. 초록 팩토리 : ComputerType.getPartFactory (“String s”) 사용법 : new ComputerType (). getFactory (“Laptop”). getComputerPart (“RAM”)
Ravi K

2
귀하의 우려를 처리하기 위해 답변을 업데이트했습니다. 실제로 추상 공장은 공장의 사실 일뿐입니다. 나는 단지 참고를 위해 이전의 예를 들었다 (실제로 구현하는 동안 독자들이 인터페이스를 처리한다고 가정). 알려 주셔서 감사합니다. 항상 개선하는 것이 좋습니다. :)
Ravi K

아니요 abstract Factory공장의 공장이 아닙니다 . 다른 콘크리트 공장으로 구현 / 확장 될 객체를 만들 abstract class거나 interface만들 수 있습니다. 코드 세부 사항은 승인 된 답변을 참조하십시오. 그에 따라 답변을 삭제하거나 수정하십시오.
Julien__

나는 그것이 왜 그렇게 이름이 붙여 졌는지 간결하게 설명하는 팩토리 메소드에 대한 설명을 좋아한다. 이 패턴에서 팩토리는 일반적으로 헬퍼 유틸리티 그룹화 인스턴스화 메소드가 아니지만 자체적으로 의미있는 클래스가 아닌 메소드입니다. 불행히도 다른 더 정교한 답변은이 시점을 놓쳤습니다.
wlnirvana

11

모든 디자인 패턴은 작성된 작업 코드를 건드리지 않도록하기 위해 노력하고 있습니다. 우리는 일단 작업 코드를 만지면 기존 작업 흐름에 결함이 있으며, 아무것도 깨지 않도록 많은 테스트가 필요하다는 것을 알고 있습니다.

팩토리 패턴은 입력 기준에 따라 객체를 생성하므로 코드와 같은 코드를 작성하지 않아도되고이 객체를 만들지 않아도됩니다. 이에 대한 좋은 예는 여행 웹 사이트입니다. 여행 웹 사이트는 여행 (비행, 기차, 버스) 또는 호텔 만 제공하거나 관광 명소 패키지를 제공 할 수 있습니다. 이제 사용자가 다음을 선택하면 웹 사이트에서 생성해야 할 객체를 결정해야합니다. 여행 또는 호텔 객체 만 생성해야합니다.

이제 포트폴리오에 다른 웹 사이트를 추가 할 계획이고 동일한 코어가 사용된다고 생각하는 경우 (예 : 택시를 검색하고 온라인으로 지불하는 카풀 링 웹 사이트) 코어에서 추상 팩토리를 사용할 수 있습니다. 이 방법으로 운전실과 카풀을 하나 더 공장에 설치할 수 있습니다.

두 공장은 서로 관련이 없으므로 서로 다른 공장에 보관할 수있는 좋은 디자인입니다.

이것이 분명하기를 바랍니다. 이 예제를 염두에두고 웹 사이트를 다시 공부하십시오. 그리고 나는 패턴을 올바르게 표현하기를 정말로 희망합니다 :).


3

이 답변에 대해서는 "Gang of Four"책을 참조하십시오.

이 책 에는 "공장", "단순 공장"또는 "가상 공장"정의가 없습니다. 일반적으로 사람들이 "공장"패턴에 대해 이야기 할 때 클래스의 특정 객체 ( "빌더"패턴이 아님) 를 생성하는 것에 대해 이야기 할 수 있습니다 . 그들은 수도 있고하지 않을 수 있습니다 "공장 방법"또는 "추상 팩토리"패턴을 참조하십시오. "공장"은 공식적인 용어가 아니기 때문에 "공장"을 구현할 수 있습니다 (일부 사람들 \ 회사 \ 커뮤니티는 자체 어휘를 ​​가질 수 있음을 명심하십시오).

이 책 에는 "Abstract Factory"및 "Factory Method"에 대한 정의 포함되어 있습니다.

다음은이 책의 정의와 왜 그렇게 혼동 될 수 있는지에 대한 간단한 설명입니다. 다른 답변에서 찾을 수 있기 때문에 코드 예제를 생략합니다.

팩토리 메소드 (GOF) : 객체를 생성하기위한 인터페이스를 정의하지만 서브 클래스가 인스턴스화 할 클래스를 결정하도록합니다. 팩토리 메소드는 클래스가 서브 클래스로 인스턴스화를 연기하도록합니다.

GOF (Abstract Factory) : 구체적인 클래스를 지정하지 않고 관련 또는 종속 객체의 패밀리를 작성하기위한 인터페이스를 제공합니다.

혼란의 근원 : 종종 "Factory Method"패턴에서 "Factory"로 사용 된 클래스를 호출 할 수 있습니다. 이 클래스는 정의에 따라 추상적입니다. 이것이 바로이 클래스를 "Abstract Factory"라고 부르는 이유입니다. 그러나 그것은 단지 클래스의 이름 일뿐입니다. "Abstract Factory"패턴 (클래스 이름! = 패턴 이름)과 혼동해서는 안됩니다. "Abstract Factory"패턴은 다릅니다- 추상 클래스를 사용 하지 않습니다 . 더 큰 객체 또는 서로 관련이 있거나 특정 방식으로 작성해야하는 객체의 일부를 작성하기위한 인터페이스 (프로그래밍 언어 인터페이스는 아님)를 정의합니다.


2
AbstractProductA, A1 and A2 both implementing the AbstractProductA
AbstractProductB, B1 and B2 both implementing the AbstractProductB

interface Factory {
    AbstractProductA getProductA(); //Factory Method - generate A1/A2
}

Factory Method를 사용하면 AbstractProductA의 A1 또는 A2를 만들 수 있습니다.

interface AbstractFactory {
    AbstractProductA getProductA(); //Factory Method
    AbstractProductB getProductB(); //Factory Method
}

그러나 Abstract Factory는 하나 이상의 팩토리 메소드 (예 : 2 팩토리 메소드)를 가지고 있으며, 이러한 팩토리 메소드를 사용하여 객체 / 관련 객체 세트를 만듭니다. Abstract Factory를 사용하면 AbstractProductA, AbstractProductB의 A1, B1 객체를 만들 수 있습니다.


0

"창조적 인 패턴에 대한 토론"(강조 광산) 섹션의 첫 두 단락에 대한 답변을 제공 하는 원본 책 Design Patterns : Elements of Reusable Object-Oriented Software 를 인용 한 사람은 없습니다 .

시스템을 생성하는 객체의 클래스로 시스템을 매개 변수화하는 두 가지 일반적인 방법이 있습니다. 한 가지 방법은 객체를 생성하는 클래스 를 서브 클래 싱 하는 것입니다 . 이것은 팩토리 메소드 (107) 패턴 사용에 해당합니다. 이 방법의 주요 단점은 제품 클래스를 변경하기 위해 새로운 서브 클래스가 필요할 수 있다는 것입니다. 이러한 변화는 연속 될 수 있습니다. 예를 들어, 제품 작성자가 팩토리 메소드로 작성되면 해당 작성자도 대체해야합니다.

시스템을 매개 변수화하는 다른 방법은 오브젝트 구성 에 더 의존 합니다. 제품 오브젝트의 클래스를 알고있는 오브젝트를 정의하고이를 시스템의 매개 변수로 만듭니다. 이는 추상 팩토리 (87), 빌더 (97) 및 프로토 타입 (117) 패턴의 주요 측면입니다. 세 가지 모두 제품 객체를 생성하는 새로운 "공장 객체"를 생성합니다. Abstract Factory에는 여러 클래스의 객체를 생성하는 팩토리 객체가 있습니다. 빌더에는 해당하는 복잡한 프로토콜을 사용하여 복잡한 제품을 증분 빌드하는 팩토리 오브젝트가 있습니다. 프로토 타입에는 프로토 타입 오브젝트를 복사하여 제품을 빌드하는 팩토리 오브젝트가 있습니다. 이 경우 프로토 타입이 제품 반품을 담당하기 때문에 팩토리 오브젝트와 프로토 타입은 동일한 오브젝트입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.