我是仿制药的新手,并且一直在试图弄清楚如何从工厂返回一个基类是通用类的实例。请参阅下面的示例代码这些问题在工厂类中突出显示:
public abstract class MyGenericBaseClass<T>
{
public string Foo()
{...}
}
public sealed class MyDerivedIntClass : MyGenericBaseClass<int>
{
}
public sealed class MyDerivedStringClass : MyGenericBaseClass<string>
{
}
public static class MyClassFactory
{
public static MyGenericBaseClass<T> CreateMyClass<T>()
{
// **********************************************
if (typeof(T) == typeof(int))
{
return new MyDerivedIntClass();
}
if (typeof(T) == typeof(string))
{
return new MyDerivedStringClass();
}
// **********************************************
}
}
我如何解决这个问题?
提前感谢
Ohgee
答案 0 :(得分:7)
在我的大多数泛型类中,我都放了一个非泛型接口。实际上,我会实现这样的事情:
public interface INonGenericInterface
{
string Foo();
}
public abstract class MyGenericBaseClass<T> : INonGenericInterface
{
public string Foo()
{...}
}
public static class MyClassFactory
{
public static INonGenericInterface CreateMyDerivedIntClass()
{
return new MyDerivedIntClass();
}
public static INonGenericInterface CreateMyDerivedStringClass()
{
return new MyDerivedStringClass();
}
}
这样,您可以清楚地说明可以创建哪些类型,并且仍然可以将调用者与具体类型分离。
当然,对于这种情况,您不一定需要非通用接口,但实际上,您很可能需要它。
答案 1 :(得分:3)
首先,不清楚为什么要创建派生自泛型类的类,而不是直接使用泛型类。也不清楚为什么你需要一个工厂而不是直接实例化你需要的任何类。这些模式非常重要且有用,通常是解决某些问题的唯一方法,但它们不是要求。你应该有充分的理由来实现它们。
但是,我会假设你确实有充分的理由说你为了简洁而省略了。使用工厂模式的目的是让一些代码实例化正确的类,而不知道正确的类。请注意,在您的示例中缺少此分隔:正在调用MyClassFactory.CreateMyClass<T> ()
的人必须知道正确的类,因为该代码必须将该类型作为通用参数传递,这决定了正确的类。如果我足够了解CreateMyClass<int> ()
,那么我就足够了解new MyDerivedIntClass ()
。
实现工厂模式有两种主要方式:静态函数和工厂类。
使用这两种方法,您需要在泛型下面“抽象”基类或接口:
public interface IMyInterface
{
string Foo ();
}
public abstract class MyGenericBaseClass<T> : IMyInterface
{
// ...
abstract /* or virtual */ string Foo ();
}
使用静态函数,您需要在某处定义委托类型(省略类范围):
// note: I'm not sure this is the correct syntax, but I think I'm in the ballpark
delegate IMyInterface MyInterfaceFactory ();
并且类可以实现它们以返回正确的类型。
public sealed class MyDerivedIntClass : MyGenericBaseClass<int>
{
// ...
static IMyInterface CreateObject () { return new MyDerivedIntClass (); }
}
public sealed class MyDerivedStringClass : MyGenericBaseClass<string>
{
// ...
static IMyInterface CreateObject () { return new MyDerivedStringClass (); }
}
将静态函数传递给实例化对象的函数:
// ... somewhere else in the code ...
// create a IMyInterface object using a factory method and do something with it
void Bar (MyInterfaceFactory factory)
{
IMyInterface mySomething = factory ();
string foo = mySomething.Foo ();
}
// ... somewhere else in the code ...
void FooBarAnInt ()
{
Bar (MyDerivedIntClass.CreateObject);
}
第二种方法是使用工厂类:
public interface IMyInterfaceFactory
{
IMyInterface CreateObject ();
}
public class MyDerivedIntFactory : IMyInterfaceFactory
{
public IMyInterface CreateObject () { return new MyDerivedIntClass (); }
}
public class MyDerivedStringFactory : IMyInterfaceFactory
{
public IMyInterface CreateObject () { return new MyDerivedStringClass (); }
}
// ... somewhere else ...
// create a IMyInterface object using a factory class and do something with it
void Bar (IMyInterfaceFactory factory)
{
IMyInterface mySomething = factory.CreateObject ();
string foo = mySomething.Foo ();
}
// ... somewhere else in the code ...
void FooBarAnInt ()
{
Bar (new MyDerivedIntFactory ());
}
请注意,您可以(也可能应该)制作工厂类单例,但这是一个不同的问题。此外,您可以使用抽象基类而不是接口;您需要根据需要添加abstract
(或virtual
)和override
。
从根本上说,有人在某处必须知道要实例化的正确对象类型。工厂对象的要点不是完全抽象出那些知识,而是从实际创建对象的代码中分离知道要创建什么类型的对象。如果您不需要这种分离,则工厂模式不是特别有用。
答案 2 :(得分:2)
我通常使用Dictionary&lt; string,Type&gt;对于我的每个工厂,我在其中注册工厂可以返回的每个可用实现类型(嗯..请参阅此信息以获取更多信息:Is a switch statement applicable in a factory method?
您可以根据自己的需要进行调整,并保持字典&lt; Type,Type&gt;所有派生类的。
...
Factories.StaticDictionary.Add(typeof(int),typeof(MyDerivedIntClass));
Factories.StaticDictionary.Add(typeof(string),typeof(MyDerivedStringClass));
...
(或让你的每个派生类自动添加到这个字典中)
那么你的工厂就是:
public static class MyClassFactory
{
public static MyGenericBaseClass<T> CreateMyClass<T>()
{
Activator.CreateInstance(
Factories.StaticDictionary[typeof(T)]);
}
}
答案 3 :(得分:0)
如果你不想进行类型检查并实例化相应的类,那么我能想到的另一种方式就是反射,它可以在有多个类时为你节省一些行但实现起来会更复杂
老实说,我认为按照现在的方式进行操作是一个完全有效的工厂实现,并且无法真正理解为什么要更改它。
答案 4 :(得分:0)
我认为这不一定是坏事。在某些时候,您需要声明您正在创建的对象的类型。通过隐藏对各种派生类的构造函数的调用,工厂允许您在一个具有已知具体类的位置。
您可以通过多种方式执行此操作 - 一组if语句,一个字典,一个企业级xml配置文件 - 但最终您需要某种方法将int的类型参数映射到MyDerivedIntClass的新实例。它可能不是很漂亮,但至少你只需要编写一次代码。
答案 5 :(得分:0)
如果你想要比其他人发布的一些例子更进一步,你应该看一下依赖注入框架。
答案 6 :(得分:0)
这条路线的主要原因是:我有一个带有'ID'属性的基类,其数据类型是在运行时确定的(string,int或long是处理的主要类型)。工厂类/方法是返回派生类,其中包含派生类ID的approprite数据类型。这就是为什么我认为我不能选择具有非通用基类/接口的原因。
在30分钟之前发现了这个想法,它在工厂方法中做到了这一点:
public static class MyClassFactory
{
public static MyGenericBaseClass<T> CreateMyClass<T>()
{
// **********************************************
if (typeof(T) == typeof(int))
{
MyGenericBaseClass<int> typedDerived = new MyDerivedIntClass();
return (MyGenericBaseClass<T>)(object)typedDerived;
}
if (typeof(T) == typeof(string))
{
MyGenericBaseClass<string> typedDerived = new MyDerivedStringClass();
return (MyGenericBaseClass<T>)(object)typedDerived;
}
// **********************************************
}
}
看起来很讨厌,尤其是双重演员,但似乎有效。你们在可维护性方面有什么看法?