3

使用泛型来实例化对象的工厂类是个好主意吗?

假设我有一个类 Animal 和一些子类(Cat、Dog 等):

abstract class Animal
{
    public abstract void MakeSound();
}

class Cat : Animal
{
    public override void MakeSound()
    {
        Console.Write("Mew mew");
    }
}

class Dog : Animal
{
    public override void MakeSound()
    {
        Console.Write("Woof woof");
    }
}

static class AnimalFactory
{
    public static T Create<T>() where T : Animal, new()
    {
        return new T();
    }
}

然后在我的代码中,我会像这样使用 AnimalFactory:

class Program
{
    static void Main(string[] args)
    {
        Dog d = AnimalFactory.Create<Dog>();
        d.MakeSound();
    }
}
4

8 回答 8

4

取决于你想用它做什么。您提供的示例并没有那么有用,因为它所做的只是调用 new() 这也是您可以在代码中执行的操作。如果您想将必须在对象创建过程中运行的逻辑放置在要创建的对象之外的某个地方,但也远离实例化对象的代码,那么工厂会更有用。

有鉴于此,这取决于您希望在工厂中运行什么逻辑,以及该逻辑是否也可以使用与工厂相同的通用约束来写入。如果没有,您可能需要一种称为抽象工厂的模式,它使用通用工厂在幕后为类型实例化正确的特定工厂,以创建手头的对象。

于 2008-12-12T14:52:41.933 回答
3

是的,非泛型类中的工厂方法通常很有用。特别是,泛型方法可以应用类型推断,而泛型类型不能。

我有时也有两层工厂 - 对于具有 2 个类型参数的泛型类型,我可能有:

class Foo
{
    static Foo<TKey, TValue> Create<TKey, TValue>(TKey key, TValue value) 
    {...}
}

class Foo<TKey>
{
    static Foo<TKey, TValue> Create<TValue>(TValue value)
    {...}
}

class Foo<TKey, TValue>
{
}

这允许:

Foo.Create(x, y);
Foo<string>.Create(y);
new Foo<string, int>(x, y);

我有一个包含更多示例和推理的博客条目。

于 2008-12-12T15:07:17.803 回答
1

我建议将工厂关注点与构造函数关注点分开。这允许不同的实例化策略:

public interface IFactory<T>
{
    T Create();
}

public class DefaultConstructorFactory<T> : IFactory<T> where T : new()
{
    public T Create()
    {
        return new T();
    }
}

public class ActivatorFactory<T> : IFactory<T>
{
    public T Create()
    {
        return Activator.CreateInstance<T>();
    }
}

public class AnimalTamer<TAnimal> where TAnimal : Animal
{
    IFactory<TAnimal> _animalFactory;

    public AnimalTamer(IFactory<TAnimal> animalFactory)
    {
        if(animalFactory == null)
        {
            throw new ArgumentNullException("animalFactory");
        }

        _animalFactory= animalFactory;
    }

    public void PutOnShow()
    {
        var animal = _animalFactory.Create();

        animal.MakeSound();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var tamer = new AnimalTamer<Tiger>(new DefaultConstructorFactory<Tiger>());

        tamer.PutOnShow();
    }
}
于 2008-12-15T03:54:48.793 回答
1

来自此的更烦人的事情之一,并且可能有一种解决方法,就是您被困在使用无参数构造函数中。据我所知,你不能这样做:

public class Factory  
{
  public static T Create<T>() where T : ParentClass, new(String)
  {

  }
}

或这个

public class Factory  
{
  public static T Create<T>() where T : ParentClass
  {
    T child = new T("hi")
  }
}

或者更有趣的是:

public class Factory  
{
  public static T Create<T>() where T : ParentClass, new()
  {
    T child;

    child = new T();

    if (child is ChildClass)
    {
      child = new ChildClass("hi");
    }

    return child;
  }
}

这可能会限制您使用某些类设计。如果您不想只调用默认构造函数,这可能是个问题。

除非有人找到解决此问题的方法。

我知道我在使用 Activator.CreateInstance() 时也遇到了这个问题,因为实际上所做的只是调用无参数构造函数。

于 2008-12-12T15:12:01.460 回答
1

在运行时之前一无所知或知之甚少的情况下,您可以通过使用打开/关闭泛型和函数来使其更加动态。似乎您对泛型有很好的掌握,所以请继续努力。

于 2011-02-19T23:28:12.383 回答
0

为构造函数使用泛型称为抽象工厂模式。

它很好,但只有在你使用它的情况下,在这个例子中你至少有一些工厂的默认值。

static class AnimalFactory
{
    public static Animal Create<T>() where T : Animal
    {
       return Create<T>("blue");
    }

    public static Animal Create<T>(string colour) where T : Animal, new()
    {
       return new T() {Colour = colour};
    }
}
于 2008-12-12T15:07:46.817 回答
0

使用泛型来实例化对象的工厂类是个好主意吗?

对于您描述的情况,不,不是。您只是为了使用模式而使用模式,并且从示例中,只会使事情变得不必要地复杂化。

现在,如果您的应用程序有多个实现某个接口的对象,并且您根据配置文件加载其中一个对象,那么是的,使用工厂创建该对象是有意义的。

这个问题的泛型部分实际上来自工厂实现本身。我没有支持代码,但这是我设想调用代码的样子:

Factory<IAwesome> awesomeFactory = new Factory<IAwesome>();
IAwesome awesomeObject = awesomeFactory.Load(configValue);
于 2008-12-12T16:26:09.017 回答
0

是的。创建设计模式(http://en.wikipedia.org)与泛型配合得很好。并且泛型可能有点复杂(http://www.artima.com/intv/genericsP.html),因此可以在某种工厂中隐藏任何构造复杂性以使客户更容易。

于 2008-12-15T05:36:33.020 回答