22

次の古典的なファクトリ パターンを使用します。

public interface IPizza
{
    decimal Price { get; }
}

public class HamAndMushroomPizza : IPizza
{
    decimal IPizza.Price
    {
        get
        {
            return 8.5m;
        }
    }
}
public abstract class PizzaFactory
{
    public abstract IPizza CreatePizza(ItalianPizzaFactory.PizzaType pizzaType);
}

public class ItalianPizzaFactory : PizzaFactory
{
    public enum PizzaType
    {
        HamMushroom,
        Deluxe,
        Hawaiian
    }

    public override IPizza CreatePizza(PizzaType pizzaType)
    {
        switch (pizzaType)
        {
            case PizzaType.HamMushroom:
                return new HamAndMushroomPizza();
            case PizzaType.Hawaiian:
                return new HawaiianPizza();
            default:
                throw new ArgumentException("The pizza type " + pizzaType + " is not recognized.");
        }
    }
}

Concrete Pizzas の 1 つ (または多数) が、構築時に具体的な実装に固有のパラメーターを必要とする場合はどうなるでしょうか。たとえば、HamAndMushroom ファクトリが MushroomType というパラメーターを必要とし、このパラメーターがオブジェクトのインスタンス化に必要であるとしましょう。

4

7 に答える 7

21

ファクトリの作成者メソッドにパラメータを追加できます。ただし、パラメーターの数が増えている場合 (私にとっては 2 ~ 3 を超える)、特にこれらのパラメーターの一部またはすべてがオプションであり、適切な既定値が設定されている場合は、代わりにファクトリをビルダーに変更することを検討してください。

これは、トッピングの (組み合わせ) が異なるだけで、通常は同じクラストを持つピザに特に適している場合があります。Builder は、「サラミ、トマト、トウモロコシ、ダブルチーズのピザ」などの一般的な注文方法を非常によくモデル化します。「事前定義された」ピザの OTOH では、ヘルパー ファクトリ メソッドを定義することができます。たとえばcreateMargaritaPizzacreateHawaiiPizza内部でビルダーを使用して、その種類のピザに固有のトッピングでピザを作成します。

于 2010-07-28T14:33:30.493 に答える
1

Map などの新しいパラメーターを渡すことができます。そして、各具体的なコンストラクターのプロパティをクエリします。次に、すべてのメソッドが同じ署名を持ちます。ただし、このソリューションでは、コンストラクターの呼び出し元は具体的なコンストラクターの特定のプロパティを知る必要があります...(結合)

于 2010-07-28T14:58:20.113 に答える
0

まず、抽象クラスに、より具体的な型のパラメーターをPizzaFactory受け取る抽象一般メソッドが含まれているのは奇妙に思えます。CreatePizzaItalianPizzaFactory.PizzaType

私が今述べた問題と投稿で述べられた問題をカバーするために、私は次のアプローチを提案します。

public struct PizzaDefinition
{
    public readonly string Tag; 
    public readonly string Name;
    public readonly string Description;
    public PizzaDefinition(string tag, string name, string description)
    {
        Tag = tag; Name = name; Description = description;
    }
}

public abstract class PizzaFactory
{
    public abstract IEnumerable<PizzaDefinition> GetMenu();
    public abstract IPizza CreatePizza(PizzaDefinition pizzaDefinition);
}


public class ItalianPizzaFactory : PizzaFactory
{
    public enum PizzaType
    {
        HamMushroom,
        Deluxe,
        Hawaiian
    }    

    public override IEnumerable<PizzaDefinition> GetMenu()
    {
        return new PizzaDefinition[] {
            new PizzaDefinition("hm:mushroom1,cheese3", "Ham&Mushroom 1", "blabla"),
            new PizzaDefinition("hm:mushroom2,cheese1", "Ham&Mushroom 2", "blabla"),
            new PizzaDefinition("dx", "Deluxe", "blabla"),
            new PizzaDefinition("Hawaian:shrimps,caramel", "Hawaian", "blabla")
        };
    }

    private PizzaType ParseTag(string tag, out object[] options){...}

    public override IPizza CreatePizza(PizzaDefinition pizzaDefinition)
    {
        object[] options;
        switch (ParseTag(pizzaDefinition.Tag, out options))
        {
            case PizzaType.HamMushroom:
                return new HamAndMushroomPizza(options);
            case PizzaType.Hawaiian:
                return new HawaiianPizza();
            default:
                throw new ArgumentException("The pizza" + pizzaDefinition.Name + " is not on the menu.");
        }
    }
}

ご覧のとおり、ParseTag()メソッドは任意の複雑さであり、プレーンテキストまたは暗号化された値を解析します。または、[タグ]フィールドは、ピザのコンテンツが少しでも変更された場合でもまったく異なるレシピを使用して、ピザのレシピテーブルに内部的にマッピングされる単純なintにすることができます。

于 2010-07-28T15:07:40.777 に答える
0

そのファクトリ クラスに別の CreatePizza() メソッドを追加する必要があります。つまり、ファクトリのユーザーは、特に HamAndMushroomPizzaFactory クラスのインスタンスを使用しない限り、これらの種類のピザを作成できません。単純に PizzaFactory への参照がある場合は、パラメーターなしのバージョンしか呼び出せず、ハムとマッシュルームのピザを一般的に作成することはできません。

于 2010-07-28T14:37:29.207 に答える
0

次のようなことを試すことができます:

interface IPizza
{
}

class Pizza1 : IPizza
{
  public Pizza1(Pizza1Parameter p)
  {
  }
}

class Pizza2 : IPizza
{
  public Pizza2(Pizza2Parameter p)
  {
  }
}

interface IPizzaParameter
{
  object Type { get; set; }
}

class Pizza1Parameter : IPizzaParameter
{
  public object Type { get; set; }
}

class Pizza2Parameter : IPizzaParameter
{
  public object Type { get; set; }
}

static class PizzaFactory
{
  public enum PizzaType
  {
    Pizza1,
    Pizza2,
  }

  public static IPizza CreatePizza(PizzaType type, IPizzaParameter param)
  {
    switch (type)
    {
      case PizzaType.Pizza1:
        return new Pizza1(param as Pizza1Parameter);
      case PizzaType.Pizza2:
        return new Pizza2(param as Pizza2Parameter);
    }

    throw new ArgumentException();
  }
}

class Program
{
  static void Main()
  {
    var param1 = new Pizza1Parameter();
    var p1 = PizzaFactory.CreatePizza(PizzaFactory.PizzaType.Pizza1, param1);
  }
}

実装固有のパラメーターを持つファクトリの私見の概念は間違っているようです。

于 2010-07-28T16:50:05.133 に答える
-2

リフレクションを使用できます:

using System.Reflection;

// ...

public override IPizza CreatePizza(PizzaType pizzaType, params object[] parameters) {
            return (IPizza)
                   Activator.CreateInstance(
                        Assembly
                             .GetExecutingAssembly()
                             .GetType(pizzaType.ToString()),
                        parameters);
        }
于 2010-07-28T14:37:58.010 に答える