수색…


비고

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

- GOF 1994

단순 공장 (자바)

팩토리는 객체 생성 코드에서 객체를 만들어야하는 코드 간의 결합을 줄입니다. 오브젝트 생성은 클래스 생성자를 호출하여 명시 적으로 수행되지 않고 호출자를 대신하여 오브젝트를 작성하는 일부 함수를 호출하여 수행됩니다. 간단한 Java 예제는 다음과 같습니다.

interface Car {
}

public class CarFactory{
  static public Car create(String s) {
    switch (s) {
    default:
    case "us":
    case "american": return new Chrysler();
    case "de":
    case "german": return new Mercedes();
    case "jp":
    case "japanese": return new Mazda();
    }
  }
}

class Chrysler implements Car {
  public String toString() { return "Chrysler"; }
}

class Mazda implements Car {
  public String toString() { return "Mazda"; }
}

class Mercedes implements Car {
  public String toString() { return "Mercedes"; }
}

public class CarEx {
  public static void main(String args[]) {
    Car car = CarFactory.create("us");
    System.out.println(car);
  }
}

이 예제에서, 사용자는 그가 필요로하는 것에 대해 힌트를주고 공장은 적절한 무언가를 자유롭게 만들 수 있습니다. 의존성 역전입니다 . Car 개념의 구현자는 사용자가 요청한 적절한 구체적인 Car 를 반환 할 수 있으며 이는 차례대로 생성 된 구체적인 객체의 세부 사항을 알지 못합니다.

이것은 factory가 어떻게 동작하는지에 대한 간단한 예입니다. 물론이 예제에서는 항상 concrete 클래스를 인스턴스화 할 수 있습니다. 그러나 패키지에 구체적인 클래스를 숨김으로써 사용자가 팩토리를 사용해야하는 등의 위험을 방지 할 수 있습니다.

위의 예제에 대한 .Net 피들 .

추상 팩토리 (C ++)

추상 팩터 리 패턴은 팩토리 함수 콜렉션을 통해 오브젝트의 일관된 콜렉션을 확보하는 f}을 제공합니다. 모든 패턴과 관련하여 커플 링은 오브젝트 세트가 생성되는 방식을 추상화하여 사용자 코드가 필요한 오브젝트의 많은 세부 사항을 인식하지 못하게합니다.

다음 C ++ 예제는 동일한 (가상의) GUI 제품군의 다른 종류의 객체를 얻는 방법을 보여줍니다.

#include <iostream>

/* Abstract definitions */
class GUIComponent {
public:
  virtual ~GUIComponent() = default;
  virtual void draw() const = 0;
};
class Frame  : public GUIComponent {};
class Button : public GUIComponent {};
class Label  : public GUIComponent {};

class GUIFactory {
public:
  virtual ~GUIFactory() = default;
  virtual std::unique_ptr<Frame> createFrame() = 0;
  virtual std::unique_ptr<Button> createButton() = 0;
  virtual std::unique_ptr<Label> createLabel() = 0;
  static std::unique_ptr<GUIFactory> create(const std::string& type);
};

/* Windows support */
class WindowsFactory : public GUIFactory {
private:
    class WindowsFrame : public Frame {
    public:
      void draw() const override { std::cout << "I'm a Windows-like frame" << std::endl; }
    };
    class WindowsButton : public Button {
    public:
      void draw() const override { std::cout << "I'm a Windows-like button" << std::endl; }
    };
    class WindowsLabel : public Label {
    public:
      void draw() const override { std::cout << "I'm a Windows-like label" << std::endl; }
    };
public:
  std::unique_ptr<Frame> createFrame() override { return std::make_unique<WindowsFrame>(); }
  std::unique_ptr<Button> createButton() override { return std::make_unique<WindowsButton>(); }
  std::unique_ptr<Label> createLabel() override { return std::make_unique<WindowsLabel>(); }
};

/* Linux support */
class LinuxFactory : public GUIFactory {
private:
    class LinuxFrame : public Frame {
    public:
      void draw() const override { std::cout << "I'm a Linux-like frame" << std::endl; }
    };
    class LinuxButton : public Button {
    public:
      void draw() const override { std::cout << "I'm a Linux-like button" << std::endl; }
    };
    class LinuxLabel : public Label {
    public:
      void draw() const override { std::cout << "I'm a Linux-like label" << std::endl; }
    };
public:
  std::unique_ptr<Frame> createFrame() override { return std::make_unique<LinuxFrame>(); }
  std::unique_ptr<Button> createButton() override { return std::make_unique<LinuxButton>(); }
  std::unique_ptr<Label> createLabel() override { return std::make_unique<LinuxLabel>(); }
};

std::unique_ptr<GUIFactory> GUIFactory::create(const string& type) {
  if (type == "windows") return std::make_unique<WindowsFactory>();
  return std::make_unique<LinuxFactory>();
}

/* User code */
void buildInterface(GUIFactory& factory) {
  auto frame = factory.createFrame();
  auto button = factory.createButton();
  auto label = factory.createLabel();

  frame->draw();
  button->draw();
  label->draw();
}

int main(int argc, char *argv[]) {
  if (argc < 2) return 1;
  auto guiFactory = GUIFactory::create(argv[1]);
  buildInterface(*guiFactory);
}

생성 된 실행 파일의 이름이 abstractfactory 경우 출력 결과는 다음과 같습니다.

$ ./abstractfactory windows
I'm a Windows-like frame
I'm a Windows-like button
I'm a Windows-like label
$ ./abstractfactory linux  
I'm a Linux-like frame
I'm a Linux-like button
I'm a Linux-like label

IoC (C #)를 사용하는 Factory의 간단한 예제

팩토리는 Inversion of Control (IoC) 라이브러리와 함께 사용할 수 있습니다.

  • 이러한 팩토리의 일반적인 사용 사례는 런타임까지 알 수없는 매개 변수 (예 : 현재 사용자)를 기반으로 객체를 생성하려는 경우입니다.
  • 이런 경우에는 이런 종류의 런타임 컨텍스트 정보를 처리하기 위해 IoC 라이브러리를 단독으로 구성하는 것이 불가능하지는 않더라도 때로는 어려울 수 있으므로 공장에서 포장 할 수 있습니다.

  • 현재 사용자가 응용 프로그램을 사용하는 사람이 될 수 있기 때문에 런타임까지 특성 (ID, 보안 등급 등)을 알 수없는 User 클래스가 있다고 가정합니다.
  • 현재 사용자를 데리고 ISecurityToken 을 가져와 사용자가 특정 작업을 수행 할 수 있는지 여부를 확인할 수 있어야합니다.
  • ISecurityToken 구현은 사용자 수준에 따라 달라집니다. 즉 ISecurityToken은 다형성을 사용합니다.

이 경우 Marker 인터페이스 를 사용하여 IoC 라이브러리에서 쉽게 식별 할 수 있도록 두 가지 구현이 있습니다. 이 경우 IoC 라이브러리는 추상화 된 IContainer 로 구성되고 식별됩니다.

또한 현대의 많은 IoC 팩토리에는 공장의 자동 생성을 허용하는 기본 기능 또는 플러그인이 있으며 아래 그림과 같이 마커 인터페이스가 필요하지 않습니다. 그러나 모든 것이 아니기 때문에이 예제는 간단하고 가장 낮은 공통 기능 개념을 충족시킵니다.

//describes the ability to allow or deny an action based on PerformAction.SecurityLevel
public interface ISecurityToken
{
    public bool IsAllowedTo(PerformAction action);
}

//Marker interface for Basic permissions
public interface IBasicToken:ISecurityToken{};
//Marker interface for super permissions
public interface ISuperToken:ISecurityToken{};

//since IBasictoken inherits ISecurityToken, BasicToken can be treated as an ISecurityToken
public class BasicToken:IBasicToken
{
     public bool IsAllowedTo(PerformAction action)
     {
         //Basic users can only perform basic actions
         if(action.SecurityLevel!=SecurityLevel.Basic) return false;
         return true;
     }
}

public class SuperToken:ISuperToken
{
     public bool IsAllowedTo(PerformAction action)
     {
         //Super users can perform all actions         
         return true;
     }
}

다음으로 우리는 IContainer 의존하는 SecurityToken 팩토리를 생성 할 것입니다.

public class SecurityTokenFactory
{
   readonly IContainer _container;
   public SecurityTokenFactory(IContainer container)
   {
      if(container==null) throw new ArgumentNullException("container");
   }

   public ISecurityToken GetToken(User user)
   {
      if (user==null) throw new ArgumentNullException("user);
      //depending on the user security level, we return a different type; however all types implement ISecurityToken so the factory can produce them.
      switch user.SecurityLevel
      {
          case Basic:
           return _container.GetInstance<BasicSecurityToken>();
          case SuperUser:
           return _container.GetInstance<SuperUserToken>();
      }
   }
}

IContainer 등록하면 다음을 수행 할 수 있습니다.

IContainer.For<SecurityTokenFactory>().Use<SecurityTokenFactory>().Singleton(); //we only need a single instance per app
IContainer.For<IBasicToken>().Use<BasicToken>().PerRequest(); //we need an instance per-request
IContainer.For<ISuperToken>().Use<SuperToken>().PerRequest();//we need an instance per-request 

소비 코드는이를 사용하여 런타임에 올바른 토큰을 얻을 수 있습니다.

readonly SecurityTokenFactory _tokenFactory;
...
...
public void LogIn(User user)
{
    var token = _tokenFactory.GetToken(user);
    user.SetSecurityToken(token);
}

이러한 방식으로 공장에서 제공하는 캡슐화와 IoC 라이브러리에서 제공하는 라이프 사이클 관리의 이점을 누릴 수 있습니다.

초록 공장

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

다음의 디자인 패턴은 창조적 인 패턴으로 분류됩니다.

추상 팩토리는 구체적인 클래스를 지정하지 않고 관련 객체의 패밀리를 작성하기위한 인터페이스를 제공하는 데 사용되며 플랫폼 특정 클래스를 숨기는 데 사용될 수 있습니다.

interface Tool {
    void use();
}

interface ToolFactory {
    Tool create();
}

class GardenTool implements Tool {

    @Override
    public void use() {
         // Do something...
    }
}

class GardenToolFactory implements ToolFactory {

    @Override
    public Tool create() {
        // Maybe additional logic to setup...
        return new GardenTool();
    }
}

class FarmTool implements Tool {

    @Override
    public void use() {
        // Do something...
    }
}

class FarmToolFactory implements ToolFactory {

    @Override
    public Tool create() {
        // Maybe additional logic to setup...
        return new FarmTool();
    }
}

그런 다음 어떤 유형의 공급자 / 생산자가 사용되어 올바른 유형의 공장 구현을 되돌릴 수있는 정보가 전달됩니다.

public final class FactorySupplier {

    // The supported types it can give you...
    public enum Type {
        FARM, GARDEN
    };

    private FactorySupplier() throws IllegalAccessException {
        throw new IllegalAccessException("Cannot be instantiated");
    }

    public static ToolFactory getFactory(Type type) {

        ToolFactory factory = null;

        switch (type) {
        case FARM:
            factory = new FarmToolFactory();
            break;
        case GARDEN:
            factory = new GardenToolFactory();
            break;
        } // Could potentially add a default case to handle someone passing in null

        return factory;
    }
}

Factory 메소드 (Java)를 구현하여 팩토리 예제

의지:

객체를 생성하기위한 인터페이스를 정의하지만 하위 클래스가 인스턴스화 할 클래스를 결정하게합니다. 팩토리 메서드를 사용하면 클래스가 하위 클래스에 대한 인스턴스 생성을 지연 할 수 있습니다.

UML 다이어그램 :

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

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

ConcreteProduct : 제품 인터페이스 구현

Creator : Factory 메서드를 선언합니다.

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

문제 문 : 게임 인터페이스를 정의하는 팩토리 메서드를 사용하여 게임 팩토리를 만듭니다.

코드 스 니펫 :

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(){
        createGame();
    }
    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(){
        createGame();
    }
    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(){
        createGame();
    }
    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]);
        System.out.println("Game="+game.getClass().getName());
    }
}

산출:

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
---------------------------------------
---------------------------------------
Create Checkers game
Opponents:2 or 3 or 4 or 6
For each opponent, place 10 coins
Start Checkers game
---------------------------------------
---------------------------------------
Create Ludo game
Opponents:2 or 3 or 4
For each opponent, place 4 coins
Create two dices with numbers from 1-6
Start Ludo game
---------------------------------------
Game=Chess

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

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

  2. Chess, Ludo, Checkers 에 구현을 제공 게임의 다른 변종이다 createGame()

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

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

  5. 게임 이름이 NotStaticFactoryDemo 명령 줄 인수로 전달됩니다.

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

사용시기 :

  1. 팩토리 : 클라이언트 / 호출자에게 객체 인스턴스화 로직을 노출하고 싶지 않을 때
  2. 추상 팩토리 : 구체적인 클래스를 지정하지 않고 관련 객체 또는 종속 객체의 패밀리에 인터페이스를 제공하려는 경우
  3. 팩토리 메소드 : 객체를 생성하기위한 인터페이스를 정의하지만 하위 클래스가 인스턴스화 할 클래스를 결정하게합니다.

다른 창조적 인 패턴과의 비교 :

  1. 디자이너가 더 많은 유연성이 필요한 곳을 발견 할 때 Factory Method (덜 복잡하고 사용자 정의가 가능하며 하위 클래스가 급증합니다)를 사용하여 Abstract Factory, Prototype 또는 Builder (더 유연하고 복잡한)로 발전하십시오.

  2. Abstract Factory 클래스는 종종 Factory Methods 로 구현되지만, Prototype을 사용하여 구현 될 수도 있습니다

더 읽을 거리 : Sourcemaking design-patterns

Flyweight Factory (C #)

간단한 단어로 :

주어진, 이미 알려진 키에 대해 항상 같은 객체를 응답으로주는 Flyweight 팩토리 . 새 키의 경우 인스턴스를 만들고 반환합니다.

팩토리 사용 :

ISomeFactory<string, object> factory = new FlyweightFactory<string, object>();

var result1 = factory.GetSomeItem("string 1");
var result2 = factory.GetSomeItem("string 2");
var result3 = factory.GetSomeItem("string 1");

//Objects from different keys
bool shouldBeFalse = result1.Equals(result2);

//Objects from same key
bool shouldBeTrue = result1.Equals(result3);

이행:

public interface ISomeFactory<TKey,TResult> where TResult : new()
{
    TResult GetSomeItem(TKey key);
}

public class FlyweightFactory<TKey, TResult> : ISomeFactory<TKey, TResult> where TResult : new()
{
    public TResult GetSomeItem(TKey key)
    {
        TResult result;
        if(!Mapping.TryGetValue(key, out result))
        {
            result = new TResult();
            Mapping.Add(key, result);
        }
        return result;
    }

    public Dictionary<TKey, TResult> Mapping { get; set; } = new Dictionary<TKey, TResult>();
}

추가 노트

나 자신의 새 인스턴스를 만드는 대신 IoC Container 사용하는 방법을이 솔루션에 추가하는 것이 좋습니다 (다른 예제에서 설명). TResult 에 대한 새로운 등록을 컨테이너에 추가 한 다음 (예에서 dictionary 대신에) TResult 에서 해결하면됩니다.

공장 방법

Factory 메소드 패턴은 클라이언트 코드를 클라이언트 코드에서 분리하기 위해 객체의 인스턴스화 로직을 추상화하는 생성 패턴입니다.

팩토리 메소드가 Abstract factory 와 같은 다른 팩토리 패턴의 구현 인 클래스에 속해있는 경우 Factory 메소드 패턴보다는 해당 클래스가 구현 한 패턴을 참조하는 것이 더 적절합니다.

팩토리 메소드 패턴은 주로 팩토리가 아닌 클래스에 속하는 팩토리 메소드를 설명 할 때 더 일반적으로 참조됩니다.

예를 들어, 도메인 개념을 나타내는 객체에 팩토리 메소드를 배치하는 것이 유리할 수 있습니다. 그 객체가 다른 객체의 작성 프로세스를 단순화하는 일부 상태를 캡슐화하는 경우입니다. 팩토리 방법은 특정 컨텍스트의 유비쿼터스 언어에보다 부합되는 디자인으로 이어질 수 있습니다.

다음은 코드 예제입니다.

//Without a factory method
Comment comment = new Comment(authorId, postId, "This is a comment");

//With a factory method
Comment comment = post.comment(authorId, "This is a comment");


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow