it-swarm-ko.tech

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

Factory 클래스 대신 객체 내에서 factory 메소드를 사용하는 것이 좋은 생각은 언제입니까?

255
jjshell

나는 수업이 '사람'이라는 관점에서 디자인 패턴에 대해 생각하는 것을 좋아하며 패턴은 사람들이 서로 이야기하는 방식입니다.

나에게 공장 패턴은 고용 기관과 같습니다. 다양한 수의 근로자가 필요한 사람이 있습니다. 이 사람은 그들이 고용 한 사람들에게 필요한 정보를 알고있을 수도 있지만 그게 전부입니다.

따라서 신입 사원이 필요할 때 고용 기관에 연락하여 필요한 것을 알려줍니다. 이제 실제로 hire 누군가에게 혜택, 자격 확인 등 많은 것들을 알아야합니다. 그러나 고용하는 사람은 이것에 대해 알 필요가 없습니다. 그.

같은 방식으로 팩토리를 사용하면 소비자는 자신이 생성 된 방법이나 종속성이 무엇인지에 대한 세부 정보를 알 필요없이 새로운 객체를 만들 수 있습니다. 실제로 원하는 정보 만 제공하면됩니다.

public interface IThingFactory
{
    Thing GetThing(string theString);
}

public class ThingFactory : IThingFactory
{
    public Thing GetThing(string theString)
    {
        return new Thing(theString, firstDependency, secondDependency);
    }
}

따라서 ThingFactory의 소비자는 소비자로부터 오는 문자열 데이터를 제외하고 Thing의 종속성에 대해 알 필요없이 Thing을 얻을 수 있습니다.

369
kyoryu

팩토리 메소드는 생성자에 대한 대안으로 고려해야합니다. 주로 생성자가 표현력이 충분하지 않은 경우입니다.

class Foo{
  public Foo(bool withBar);
}

다음과 같이 표현 적이 지 않습니다.

class Foo{
  public static Foo withBar();
  public static Foo withoutBar();
}

팩토리 클래스는 객체를 생성하기 위해 복잡한 프로세스가 필요할 때, 구성에 실제 클래스에 대해 원하지 않는 종속성이 필요할 때, 다른 객체를 생성해야 할 때 유용합니다.

88
Rasmus Faber

개인적으로 이해하기 위해 별도의 Factory 클래스를 찾는 한 가지 상황은 만들려는 최종 객체가 다른 여러 객체에 의존하는 경우입니다. 예를 들어, PHP에서 : House 객체가 있고 KitchenLivingRoom 객체가 있고 LivingRoom 객체가 TV 내부에도 물체가 있습니다.

이를 달성하는 가장 간단한 방법은 각 객체가 구성 메소드에서 하위를 작성하는 것입니다. 그러나 속성이 상대적으로 중첩 된 경우 House_ 작성에 실패하면 실패한 항목을 정확하게 분리하는 데 약간의 시간이 소요됩니다.

대안은 다음을 수행하는 것입니다 (멋진 용어가 마음에 들면 의존성 주입).

$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);

여기서 House 작성 프로세스가 실패하면 볼 곳이 하나 뿐이지 만 새 House을 원할 때마다이 청크를 사용해야하는 것은 편리하지 않습니다. 공장을 입력하십시오 :

class HouseFactory {
    public function create() {
        $TVObj = new TV($param1, $param2, $param3);
        $LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
        $KitchenroomObj = new Kitchen($param1, $param2);
        $HouseObj = new House($LivingroomObj, $KitchenroomObj);

        return $HouseObj;
    }
}

$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();

여기 팩토리 덕분에 House 작성 프로세스가 추상화됩니다 (House을 작성하려고 할 때 모든 단일 종속성을 작성하고 설정할 필요가 없음). 중앙 집중식으로 유지 관리가 용이합니다. 별도의 팩토리를 사용하는 것이 유리할 수있는 다른 이유가 있지만 (예 : 테스트 가능성) Factory 클래스가 유용한 방법을 가장 잘 설명하기 위해이 특정 사용 사례를 찾습니다.

71
Mahn

팩토리 또는 팩토리 방법을 사용하여 아이디어를 명확하게 구별하는 것이 중요합니다. 둘 다 상호 배타적 인 다른 종류의 객체 생성 문제를 해결하기위한 것입니다.

"공장 방법"에 대해 구체적으로 설명하겠습니다.

첫 번째는 추가 응용 프로그램 개발에 사용될 라이브러리 또는 API를 개발할 때 팩토리 방법이 작성 패턴에 가장 적합한 선택 중 하나라는 것입니다. 뒤에 이유; 필요한 기능을 가진 객체를 생성 할 때 객체 유형이 결정되지 않은 상태이거나 동적 매개 변수가 전달되는 것으로 결정됨.

요점은 팩토리 패턴 자체를 사용하여 거의 동일하게 달성 할 수 있지만 팩토리 패턴이 강조 표시된 문제에 사용될 경우 하나의 큰 단점이 시스템에 도입 될 것입니다. 다른 객체 (하위 클래스 객체)를 만드는 논리는 미래에는 다른 플랫폼을 위해 라이브러리의 기능을 확장해야 할 때 일부 비즈니스 조건에 특정해야합니다 (보다 기술적으로는 기본 인터페이스 또는 추상 클래스의 하위 클래스를 더 추가해야 팩토리가 기존 객체 외에도 해당 객체를 반환합니다) 일부 동적 매개 변수를 기반으로) 비용이 많이 들며 디자인 관점에서 좋지 않은 팩토리 클래스의 논리를 변경 (확장) 할 때마다. 다른 한편으로, "factory method"패턴을 사용하여 동일한 작업을 수행하는 경우 추가 기능 (하위 클래스)을 생성하고 기본 코드를 변경할 필요가없는 주입에 의해 동적으로 등록하면됩니다.

interface Deliverable 
{
    /*********/
}

abstract class DefaultProducer 
{

    public void taskToBeDone() 
    {   
        Deliverable deliverable = factoryMethodPattern();
    }
    protected abstract Deliverable factoryMethodPattern();
}

class SpecificDeliverable implements Deliverable 
{
 /***SPECIFIC TASK CAN BE WRITTEN HERE***/
}

class SpecificProducer extends DefaultProducer 
{
    protected Deliverable factoryMethodPattern() 
    {
        return new SpecificDeliverable();
    }
}

public class MasterApplicationProgram 
{
    public static void main(String arg[]) 
    {
        DefaultProducer defaultProducer = new SpecificProducer();
        defaultProducer.taskToBeDone();
    }
}
18
Prakash Chhipa

다음과 같은 경우 factory methods 객체 내부를 사용하는 것이 좋습니다.

  1. 객체의 클래스는 정확히 어떤 서브 클래스를 생성해야하는지 모른다
  2. 객체의 클래스는 객체가 생성하는 객체가 하위 클래스에 의해 지정되도록 설계되었습니다.
  3. Object의 클래스는 자신의 임무를 보조 서브 클래스에 위임하고 정확한 클래스가이 의무를 수행 할 것인지 모릅니다

다음과 같은 경우 abstract factory class를 사용하는 것이 좋습니다.

  1. 객체는 내부 객체를 만들고 디자인하는 방법에 의존해서는 안됩니다
  2. 연결된 개체 그룹을 함께 사용해야하며이 제약 조건을 제공해야합니다.
  3. 개체는 부모 개체의 일부가 될 수있는 여러 연결된 개체 그룹 중 하나에 의해 구성되어야합니다.
  4. 인터페이스 만 보여 주지만 구현은 아닌 자식 객체를 공유해야합니다.
14
Dzianis Yafimau

매개 변수 유형은 동일하지만 동작이 다른 여러 "생성자"가 필요한 경우에도 유용합니다.

14
Rik

에서 UML

enter image description here

Product : Factory 메소드가 생성하는 객체의 인터페이스를 정의합니다.

ConcreteProduct : 제품 인터페이스 구현

작성자 : 팩토리 메서드를 선언합니다.

ConcreateCreator : ConcreteProduct의 인스턴스를 반환하는 Factory 메서드를 구현합니다.

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

코드 스 니펫 :

import Java.util.HashMap;


/* Product interface as per UML diagram */
interface Game{
    /* createGame is a complex method, which executes a sequence of game steps */
    public void createGame();
}

/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
    public Chess(){

    }
    public void createGame(){
        System.out.println("---------------------------------------");
        System.out.println("Create Chess game");
        System.out.println("Opponents:2");
        System.out.println("Define 64 blocks");
        System.out.println("Place 16 pieces for White opponent");
        System.out.println("Place 16 pieces for Black opponent");
        System.out.println("Start Chess game");
        System.out.println("---------------------------------------");
    }
}
class Checkers implements Game{
    public Checkers(){

    }
    public void createGame(){
        System.out.println("---------------------------------------");
        System.out.println("Create Checkers game");
        System.out.println("Opponents:2 or 3 or 4 or 6");
        System.out.println("For each opponent, place 10 coins");
        System.out.println("Start Checkers game");
        System.out.println("---------------------------------------");
    }
}
class Ludo implements Game{
    public Ludo(){

    }
    public void createGame(){
        System.out.println("---------------------------------------");
        System.out.println("Create Ludo game");
        System.out.println("Opponents:2 or 3 or 4");
        System.out.println("For each opponent, place 4 coins");
        System.out.println("Create two dices with numbers from 1-6");
        System.out.println("Start Ludo game");
        System.out.println("---------------------------------------");
    }
}

/* Creator interface as per UML diagram */
interface IGameFactory {
    public Game getGame(String gameName);
}

/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {

     HashMap<String,Game> games = new HashMap<String,Game>();
    /*  
        Since Game Creation is complex process, we don't want to create game using new operator every time.
        Instead we create Game only once and store it in Factory. When client request a specific game, 
        Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
    */

    public GameFactory(){

        games.put(Chess.class.getName(),new Chess());
        games.put(Checkers.class.getName(),new Checkers());
        games.put(Ludo.class.getName(),new Ludo());        
    }
    public Game getGame(String gameName){
        return games.get(gameName);
    }
}

public class NonStaticFactoryDemo{
    public static void main(String args[]){
        if ( args.length < 1){
            System.out.println("Usage: Java FactoryDemo gameName");
            return;
        }

        GameFactory factory = new GameFactory();
        Game game = factory.getGame(args[0]);
        if ( game != null ){                    
            game.createGame();
            System.out.println("Game="+game.getClass().getName());
        }else{
            System.out.println(args[0]+  " Game does not exists in factory");
        }           
    }
}

산출:

Java NonStaticFactoryDemo Chess
---------------------------------------
Create Chess game
Opponents:2
Define 64 blocks
Place 16 pieces for White opponent
Place 16 pieces for Black opponent
Start Chess game
---------------------------------------
Game=Chess

이 예는 Factory을 구현하여 FactoryMethod 클래스를 보여줍니다.

  1. Game은 모든 유형의 게임을위한 인터페이스입니다. 복잡한 방법을 정의합니다 : createGame()

  2. Chess, Ludo, Checkers은 (는) createGame()에 구현을 제공하는 다양한 게임 변형입니다.

  3. public Game getGame(String gameName)은 (는) FactoryMethod 클래스에서 IGameFactory입니다.

  4. GameFactory은 생성자에서 다른 유형의 게임을 미리 만듭니다. IGameFactory 팩토리 메소드를 구현합니다.

  5. game Name이 NotStaticFactoryDemo에 명령 줄 인수로 전달됩니다.

  6. getGameGameFactory은 (는) 게임 이름을 받아들이고 해당 Game 개체를 반환합니다.

공장 :

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

공장 방법

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

사용 사례 :

사용시기 : Client은 (는) 런타임에 어떤 구체적인 클래스를 만들어야하는지 모르지만 작업을 수행 할 클래스를 얻으려고합니다.

8
Ravindra babu

정말 맛의 문제입니다. 팩토리 클래스는 필요에 따라 추상화/인터페이스 될 수 있지만 팩토리 메소드는 더 가볍습니다 (또한 정의 된 유형이 없기 때문에 테스트 할 수있는 경향이 있지만 서비스와 유사하게 잘 알려진 등록 포인트가 필요함) 로케이터이지만 공장 방법을 찾는 경우).

5
Brad Wilson

팩토리 클래스는 리턴하는 오브젝트 유형에 개인 생성자가있는 경우, 다른 팩토리 클래스가 리턴하는 오브젝트에 다른 특성을 설정하거나 특정 팩토리 유형이 리턴 콘크리트 유형과 결합 된 경우에 유용합니다.

WCF 서로 다른 상황에서 ServiceHostFactory 클래스를 사용하여 ServiceHost 객체를 검색합니다. 표준 ServiceHostFactory는 IIS에서 . svc 파일에 대한 ServiceHost 인스턴스를 검색하는 데 사용되지만 WebScriptServiceHostFactory는 직렬화를 리턴하는 서비스에 사용됩니다. JavaScript 클라이언트에. ADO.NET Data Services에는 고유 한 특수 DataServiceHostFactory가 있으며 ASP.NET에는 서비스에 개인 생성자가 있으므로 ApplicationServicesHostFactory가 있습니다.

팩토리를 소비하는 클래스가 하나 뿐인 경우 해당 클래스 내에서 팩토리 메소드를 사용할 수 있습니다.

4
Mark Cidade

주문 및 고객 클래스를 설계해야하는 시나리오를 고려하십시오. 단순성과 초기 요구 사항을 위해 Order 클래스에 대한 팩토리가 필요하지 않으며 많은 'new Order ()'문으로 응용 프로그램을 채우십시오. 상황이 잘 작동합니다.

이제 고객 연관 (새로운 종속성)없이 주문 오브젝트를 인스턴스화 할 수 없다는 새로운 요구 사항이 제시되었습니다. 이제 다음 사항을 고려해야합니다.

1- 새로운 구현에만 작동하는 생성자 과부하를 만듭니다. (받아 드릴 수없는). 2- Order () 서명을 변경하고 호출 할 때마다 변경합니다. (좋은 습관과 진정한 고통이 아닙니다).

대신 주문 클래스에 대한 팩토리를 생성 한 경우 한 줄의 코드 만 변경하면됩니다. 거의 모든 집계 연관에 대해 팩토리 클래스를 제안합니다. 희망이 도움이됩니다.

3
Muhammad Awais

소스 메이킹 웹 사이트에 따르면 의도는 다음과 같습니다.

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

  • "가상"생성자 정의.

  • 새로운 운영자는 해로운 것으로 간주했습니다.

사용 방법의 예 :

abstract class AbstractFactoryMethod {
    abstract function makePHPBook($param);
}

class OReillyFactoryMethod extends AbstractFactoryMethod
{
    function makePHPBook($param)
    {
        $book = NULL;  
        switch ($param) {
            case "us":
                $book = new OReillyPHPBook();
            break;
            // Other classes...
            case "other":
                $book = new SamsPHPBook();
            break;
            default:
                $book = new OReillyPHPBook();
            break;        
    }

    return $book;
}

그리고 테스트 :

function testFactoryMethod($factoryMethodInstance)
{
    $phpUs = $factoryMethodInstance->makePHPBook("us");
    echo 'us php Author: '.$phpUs->getAuthor();
    echo 'us php Title: '.$phpUs->getTitle();
}

echo 'Testing OReillyFactoryMethod';
$factoryMethodInstance = new OReillyFactoryMethod();
testFactoryMethod($factoryMethodInstance);
3
Alexander Beat

작업해야하는 객체에 대해 객체 생성을 하위 클래스로 연기하는 모든 클래스를 팩토리 패턴의 예로 볼 수 있습니다.

https://stackoverflow.com/a/49110001/5041 의 또 다른 답변에서 자세히 언급했습니다.

1
nits.kk

나는 공장을 도서관의 개념에 비유한다. 예를 들어 숫자로 작업하기위한 라이브러리와 모양으로 작업하기위한 라이브러리를 가질 수 있습니다. 이러한 라이브러리의 기능을 논리적으로 명명 된 디렉토리에 Numbers 또는 Shapes로 저장할 수 있습니다. 이것들은 정수, 부동 수, 더불, 긴 또는 직사각형, 원, 삼각형, 오각형을 포함 할 수있는 일반적인 유형입니다.

팩토리 페터는 다형성, 의존성 주입 및 제어 반전을 사용합니다.

팩토리 패턴의 명시된 목적은 다음과 같습니다. Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

운영 체제 또는 프레임 워크를 구축하고 모든 개별 구성 요소를 구축한다고 가정 해 봅시다.

다음은 PHP의 팩토리 패턴 개념에 대한 간단한 예입니다. 나는 100 %가 아닐 수도 있지만 간단한 예를 제공하기위한 것입니다. 나는 전문가가 아닙니다.

class NumbersFactory {
    public static function makeNumber( $type, $number ) {
        $numObject = null;
        $number = null;

        switch( $type ) {
            case 'float':
                $numObject = new Float( $number );
                break;
            case 'integer':
                $numObject = new Integer( $number );
                break;
            case 'short':
                $numObject = new Short( $number );
                break;
            case 'double':
                $numObject = new Double( $number );
                break;
            case 'long':
                $numObject = new Long( $number );
                break;
            default:
                $numObject = new Integer( $number );
                break;
        }

        return $numObject;
    }
}

/* Numbers interface */
abstract class Number {
    protected $number;

    public function __construct( $number ) {
        $this->number = $number;
    }

    abstract public function add();
    abstract public function subtract();
    abstract public function multiply();
    abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Integer Implementation */
class Integer extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Short Implementation */
class Short extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Double Implementation */
class Double extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}
/* Long Implementation */
class Long extends Number {
    public function add() {
        // implementation goes here
    }

    public function subtract() {
        // implementation goes here
    }

    public function multiply() {
        // implementation goes here
    }

    public function divide() {
        // implementation goes here
    }
}

$number = NumbersFactory::makeNumber( 'float', 12.5 );
0
Robert Rocha

팩토리 클래스는 더 무겁지만 특정 장점을 제공합니다. 여러 개의 원시 데이터 소스에서 오브젝트를 빌드해야하는 경우 한 곳에서 빌딩 로직 (및 데이터 집계) 만 캡슐화 할 수 있습니다. 거기에서 객체 인터페이스에 관계없이 추상적으로 테스트 할 수 있습니다.

특히 유용한 ORM을 대체 할 수없고 DB 테이블 조인 또는 저장 프로 시저에서 많은 개체를 효율적으로 인스턴스화하려는 경우에 유용한 패턴을 찾았습니다.

0
jonfm

AbstractFactory 예제

    TypeImpl<String> type = new TypeImpl<>();
    type.addType("Condition");
    type.addType("Hazardous");

    AbstractTypeFactory<String, Tag> tags = new AbstractTypeFactory<String, Tag>(type) {

        @Override
        public Tag create(String string) {
            String tp = type.find(string);

            switch (tp) {
                case "Hazardous":
                    return new HazardousTag();
                case "Condition":
                    return new ConditionTag();
                default:
                    return null;
            }
        }
    };

    Tag tagHazardous = tags.create("Hazardous");
    Tag tagCondition = tags.create("Condition");

}
0
Vahe Gharibyan

사용 측면에서 다른 객체를 생성하려는 경우 그것은 쓸모있다.

public class factoryMethodPattern {
      static String planName = "COMMERCIALPLAN";
      static int units = 3;
      public static void main(String args[]) {
          GetPlanFactory planFactory = new GetPlanFactory();
          Plan p = planFactory.getPlan(planName);
          System.out.print("Bill amount for " + planName + " of  " + units
                        + " units is: ");
          p.getRate();
          p.calculateBill(units);
      }
}

abstract class Plan {
      protected double rate;

      abstract void getRate();

      public void calculateBill(int units) {
            System.out.println(units * rate);
      }
}

class DomesticPlan extends Plan {
      // @override
      public void getRate() {
            rate = 3.50;
      }
}

class CommercialPlan extends Plan {
      // @override
      public void getRate() {
            rate = 7.50;
      }
}

class InstitutionalPlan extends Plan {
      // @override
      public void getRate() {
            rate = 5.50;
      }
}

class GetPlanFactory {

      // use getPlan method to get object of type Plan
      public Plan getPlan(String planType) {
            if (planType == null) {
                  return null;
            }
            if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
                  return new DomesticPlan();
            } else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
                  return new CommercialPlan();
            } else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
                  return new InstitutionalPlan();
            }
            return null;
      }
}
0
Samet ÖZTOPRAK