サーチ…


備考

具体的なクラスを指定せずに、関連オブジェクトまたは依存オブジェクトのファミリを作成するためのインタフェースを提供します。

- GOF 1994

シンプルファクトリ(Java)

ファクトリは、オブジェクト作成コードからオブジェクトを作成する必要があるコード間の結合を減少させます。オブジェクトの作成は、クラスコンストラクタを呼び出すことではなく、呼び出し元のためにオブジェクトを作成する関数を呼び出すことによって明示的に行われます。単純な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順番に建てられたコンクリートのオブジェクトの詳細を知らないユーザーが要求を。

これは工場の仕組みの簡単な例ですが、この例では具体的なクラスをインスタンス化することは常に可能です。パッケージに具体的なクラスを隠すことによって、ユーザーが工場を使用するように強制することができます。

上記の例のための.Net Fiddle

抽象ファクトリ(C ++)

抽象ファクトリパターンは、ファクトリ関数のコレクションを通じてオブジェクトの一貫したコレクションを取得する方法を提供します。どのパターンでも、一連のオブジェクトの作成方法を抽象化することによってカップリングが削減されるため、ユーザーコードは必要なオブジェクトの多くの詳細を認識しません。

次の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は多態性を使用します。

この場合、 マーカーインターフェイスを使用してIoCライブラリにそれらを簡単に識別できるように、2つの実装が用意されています。この場合の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

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;
    }
}

ファクトリメソッド(Java)を実装したファクトリの例

意図:

オブジェクトを作成するためのインタフェースを定義しますが、サブクラスがどのクラスをインスタンス化するかを決定させます。ファクトリメソッドを使用すると、クラスがサブクラスにインスタンス化を遅延させることができます。

UML図:

ここに画像の説明を入力

プロダクト:ファクトリメソッドが作成するオブジェクトのインタフェースを定義します。

ConcreteProduct:製品インターフェイスを実装する

作成者:ファクトリメソッドを宣言する

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)あるFactoryMethodIGameFactoryクラス

  4. GameFactoryは、コンストラクタで異なる種類のゲームを事前に作成します。 IGameFactoryファクトリメソッドを実装しています。

  5. ゲーム名は、コマンドライン引数としてNotStaticFactoryDemo渡されますNotStaticFactoryDemo

  6. getGame GameFactoryはゲーム名を受け取り、対応するGameオブジェクトを返します。

いつ使用する:

  1. Factory :オブジェクトのインスタンス化ロジックをクライアント/呼び出し元に公開したくない場合
  2. 抽象ファクトリ :具体的なクラスを指定せずに、関連オブジェクトまたは依存オブジェクトのファミリにインターフェイスを提供する場合
  3. ファクトリメソッド:オブジェクトを作成するためのインタフェースを定義するが、サブクラスがどのクラスをインスタンス化するかを決定させる

他のパターンとの比較:

  1. デザイナーがより柔軟性が必要な場所を発見すると、 ファクトリメソッドを使用したデザインの開始(複雑性が低く、カスタマイズ性が高く、サブクラスが広がります)、 抽象的なファクトリ、プロトタイプ、またはビルダーに移行します

  2. Abstract FactoryクラスはFactoryメソッドで実装されることがよくありますが、 Prototypeを使用して実装することもできます

さらに読むための参考文献: ソースメイキングデザインパターン

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ではなく)。

ファクトリメソッド

ファクトリメソッドパターンは、クライアントコードをクライアントコードから切り離すために、オブジェクトのインスタンス化ロジックを抽象化するための作成パターンです。

ファクトリメソッドが抽象ファクトリのような別のファクトリパターンの実装であるクラスに属する場合、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