使用带有派生类的工厂模式,接受不同数量的参数

时间:2016-04-27 10:42:22

标签: c# .net visual-studio design-patterns factory-pattern

我使用以下factory pattern:

using System;

class Program
{
    abstract class Position
    {
    public abstract string Title { get; }
    }

    class Manager : Position
    {
    public override string Title
    {
        get
        {
        return "Manager";
        }
    }
    }

    class Clerk : Position
    {
    public override string Title
    {
        get
        {
        return "Clerk";
        }
    }
    }

    class Programmer : Position
    {
    public override string Title
    {
        get
        {
        return "Programmer";
        }
    }
    }

    static class Factory
    {
    /// <summary>
    /// Decides which class to instantiate.
    /// </summary>
    public static Position Get(int id)
    {
        switch (id)
        {
        case 0:
            return new Manager();
        case 1:
        case 2:
            return new Clerk();
        case 3:
        default:
            return new Programmer();
        }
    }

使用此模式的方法来自同一来源的示例:

static void Main()
{
for (int i = 0; i <= 3; i++)
{
    var position = Factory.Get(i);
    Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
}
}

如果我的派生类为其构造函数使用不同数量的参数,我是否应该使用此模式?

我需要做的可能的修改是在实例化工厂时:

var position = Factory.Get(i);

我可能需要传递所有派生类的参数,无论它们是否会使用它们:

var position = Factory.Get(i, param1, param2, param3);

并且需要修改switch语句:

public static Position Get(int id, param1, param2, param3) //HERE IS THE MODIFIED PARAM LIST
{
    switch (id)
    {
    case 0:
        return new Manager(param1); //MODIFIED
    case 1:
    case 2:
        return new Clerk(param2, param3); //MODIFIED
    case 3:
    default:
        return new Programmer(param3); //MODIFIED
    }
}

我对工厂模式所做的修改是否会破坏模式,我应该使用不同的模式来创建对象吗?

4 个答案:

答案 0 :(得分:1)

这个例子太过愚蠢,但是,你可以。

那就是说,这个例子可能会导致一些问题,因为程序员可能会对如何使用工厂感到困惑。例如,假设您必须定义一个GUI来创建位置:GUI是否会询问用户所有3个param值,即使它们对首先定义的位置没有意义?如果你回答“是”,用户会感到困惑,如果你回答“否”,那么工厂就不应该是一个黑盒子了。

我使用这种方法的一个例子是计费;我的申请将同月的服务按批次计费给很多人。有些人按月份的自然天数收费,有些人按可用日数收费。由于获得可用的日子有点慢(它必须在本地和国家假日咨询数据库)我缓存它并将其传递给需要它的实例。

就像(Java):

public class BillerFactory {
  private HashMap<Date, ListOfHolidaysInMonth> holidayCache =
     new HashMap<>();

  ...

  public getBiller(BillingType billingType, Date firstOfMonth) {
    switch (billingType) {
      case BillingType.NATURAL:
         return new NaturalBiller(firstOfMonth);
      case BillingType.LABORAL:
         ListOfHoliday holidays = this.holidayCache.get(firstOfMonth);
         if (holidays == null) {
            holidays = this.calculateHolidays(firstOfMonth);
            holidayCache.put(firstOfMonth, holidays);
         }
         return new LaboralBiller(firstOfMonth, holidays);
       }
     }

TLDR:问题不在于构造函数有不同的参数,但在您的示例中,您强制客户端提供可能没有意义的数据。

答案 1 :(得分:1)

下面的代码将使用Position类中的默认构造函数。

         static void Main(string[] args)
        {
            Manager mngr = new Manager();
        }
    }
    public abstract class Position
    {
        public abstract string Title { get; }
        public Position()
        {
        }
    }
    public class Manager : Position
    {
        public override string Title
        {
            get
            {
                return "Manager";
            }
        }
    }

答案 2 :(得分:1)

我没有足够的背景建议一个好的选择,但你的样本使用起来不舒服:工厂模式的想法是封装对象的创建,但因为你有一个不同的集合对于不同对象的参数,您必须知道具体实现之间的一些差异,或者您必须每次都提供无用的数据。也许只使用构造函数会更好吗?

答案 3 :(得分:1)

工厂的使用是抽象出如何创建对象的要求,以便允许客户端询问对象,而无需了解的详细信息。

该模式最常见的用途之一是让应用程序基于可能返回MSSQL,SQLite,MySQL等连接的密钥创建数据库连接。只要它支持所有必需的操作,客户端就不关心实现是什么。

因此,客户端应该完全不知道所需的参数。

这是怎么做的。

我稍微扩展了Position类:

abstract class Position
{
    public abstract string Title { get; }
}

class Manager : Position
{
    public Manager(string department) { }
    public override string Title => "Manager";
}

class Clerk : Position
{
    public override string Title => "Clerk";
}

class Programmer : Position
{
    public Programmer(string language) { }
    public override string Title => "Programmer";
}

现在我已经创建了这样的Factory类:

static class Factory
{
    private static Dictionary<int, Func<Position>> _registry =
        new Dictionary<int, Func<Position>>();

    public static void Register(int id, Func<Position> factory)
    {
        _registry[id] = factory;
    }

    public static Position Get(int id)
    {
        return _registry[id].Invoke();
    }
}

然后它变得易于使用工厂。当您初始化应用程序时,您将编写此类代码:

Factory.Register(1, () => new Manager("Sales"));
Factory.Register(2, () => new Clerk());
Factory.Register(3, () => new Programmer("C#"));

现在,稍后,当客户端代码想要一个Position对象时,它只需要这样做:

var position = Factory.Get(3);

在我输出position.Title的测试中,我将Programmer打印到控制台。