C# 我应该使用什么样的创作模式?
我的课程有两个班;两者都派生自相同的基类C# 我应该使用什么样的创作模式?,c#,design-patterns,creation-pattern,C#,Design Patterns,Creation Pattern,我的课程有两个班;两者都派生自相同的基类 class A : MyBase { internal A(InitVal initVal) } class B : MyBase { internal B(InitVal initVal) } InitVal是通过构造函数注入的另一个类。此类用于内部使用。由于内部构造函数,用户无法直接创建类A和B的实例。相反,我创建了创建这些对象的方法 class Initiator { InitVal initVal; publ
class A : MyBase
{
internal A(InitVal initVal)
}
class B : MyBase
{
internal B(InitVal initVal)
}
InitVal
是通过构造函数注入的另一个类。此类用于内部使用。由于内部构造函数,用户无法直接创建类A
和B
的实例。相反,我创建了创建这些对象的方法
class Initiator
{
InitVal initVal;
public T CreateObject<T>(ObjectInstance objectInstance) where T : MyBase
{
MyBase myBase = null;
switch(objectInstance)
{
case ObjectInstance.A:
myBase = new A(initVal);
break;
case ObjectInstance.B:
myBase = new B(initVal);
break;
}
return (T)myBase;
}
...
}
第一个用于T
通用值,第二个用于枚举值。
我想避免这种情况
2) 由于用户必须两次指定对象的类型,所以有可能出错
A a = initiator.CreateObject<A>(ObjectInstance.B);
但是,我不确定如何实现此方法以创建适当的实例。由于您将该方法指定为泛型方法,我希望您可能在编译期间已经知道要获取的类型。。所以我会选择这样的方式:
class Initiator
{
public T CreateObject<T>(ObjectInstance objectInstance) where T : MyBase, new()
{
T newInstance = new T();
newInstance.Value = initVal;
return newInstance;
}
...
}
这不仅更干净、更短,而且更不容易出错,因为每次添加新类型时都不需要同时编辑枚举和方法体。但是,对于特定于子类型的逻辑,它的灵活性较低
注意:如果您真的想要像现在这样使用带参数的构造函数,您仍然可以使用这种方法,但是您需要使用反射(检查激活器)或lambdas
当然,只有在编译期间决定类型,或者只想将此决定委托给第三方库时,这才有意义,例如:
switch(chosenType){
case ObjectInstance.A:
instance = initiator.CreateObject<A>();
...
在我看来,你并没有从尝试使这种通用性中获得任何优势。您需要知道调用站点返回值的具体类型 因此,为什么不简单一点,就这么做呢
public class Initiator
{
InitVal initVal;
public A CreateA()
{
return new A(initVal);
}
public B CreateB()
{
return new B(initVal);
}
}
代码的一个明显问题是枚举,这是不必要的,因为
typeof(T)
已经为您提供了适当的类型:
class Initiator
{
readonly Dictionary<Type, Func<MyBase>> _dict = new Dictionary<Type, Func<MyBase>>();
internal Initiator(InitVal initVal)
{
// initialize your "service locator".
// it's cool that different types can have different constructors,
// and people who call CreateObject don't need to know this.
_dict[typeof(A)] = (Func<MyBase>)(() => new A(initVal));
_dict[typeof(B)] = (Func<MyBase>)(() => new B(initVal, someOtherStuff));
}
public T CreateObject<T>() where T : MyBase
{
var ctor = _dict[typeof(T)];
return (T)ctor();
}
}
类启动器
{
只读词典_dict=新词典();
内部启动器(InitVal InitVal)
{
//初始化“服务定位器”。
//很酷,不同类型可以有不同的构造函数,
//调用CreateObject的人不需要知道这一点。
_dict[typeof(A)]=(Func)(()=>newa(initVal));
_dict[typeof(B)]=(Func)(()=>newb(initVal,someOtherStuff));
}
public T CreateObject(),其中T:MyBase
{
变量向量=_dict[typeof(T)];
返回(T)向量();
}
}
或者,如果不知道类型,可以传递枚举,但返回类型应该是接口/基类(最好是接口):
//更可能的情况是,您可能不需要泛型方法
公共IMyBase CreateObject(ObjectInstance ObjectInstance)
{
//当然,字典会将枚举值映射到Func
变量向量=_dict[反对];
返回向量();
}
现在您有了一个简单的“穷人”DI类,名为Initiator
,因此我想知道您的DI框架(注入InitVal
)是否也能注入a
和B
实例。这可能是真的,因为DI纯粹主义者会告诉你没有工厂的地方,你的代码中没有new
关键字
顺便说一句,
ObjectInstance
对于枚举来说是一个非常非常糟糕的名字。我是通过以下方式实现的:
class A : IMyType
{
internal A(InitVal initVal)
}
class B : IMyType
{
internal B(InitVal initVal)
}
class Initiator
{
InitVal initVal = .....;
public T CreateObject<T>() where T : IMyType
{
IMyType myType = null;
if(typeof(T) == typeof(A))
myType = new A(initVal);
else if(typeof(T) == typeof(B))
myType = new B(initVal);
else
throw new MyException("Type is not configured.");
return (T)myType;
}
...
}
A类:IMyType
{
内部A(InitVal InitVal)
}
B类:IMyType
{
内部B(InitVal InitVal)
}
类发起人
{
InitVal InitVal=。。。。。;
public T CreateObject(),其中T:IMyType
{
IMyType myType=null;
如果(类型(T)=类型(A))
myType=新的A(初始值);
否则如果(类型(T)=类型(B))
myType=新的B(初始值);
其他的
抛出新的MyException(“类型未配置”);
返回(T)myType;
}
...
}
这解决了我在问题中提到的问题。但是,它带来了新的问题这违反了SOLID的开闭原则。最后一个
else
块处理手动错误(如果有)。无论如何,它只适用于我的具体情况一般不推荐。如果您不能更改A
或B
,那么人们仍然可以创建A
和/或B
的实例。那么,为什么还要为整个Initiator
类操心呢?如果您不想让API的使用者实例化类,我会将构造函数设置为内部的。您的代码没有那么难看,这正是工厂方法所做的:它基于某个不同的值创建类型的实例,在您的例子中是enum。如果添加了一个新类,您也可以通过一个配置文件来实现这一点,这样就不必更改源代码。唯一难看的是缩进。当前Initiator
类的一个问题是,用户现在必须构造Initiator
的实例,并且必须以某种方式设置initVal
。字段是如何设置的?另一种方式,稍微干净一点,就是简单地有两种方法来创建a或B,第三方方法将选择其中一种。或者将其作为一个方法实现,并通过指定泛型类型来确定类型。否则,它只是一个工厂方法,看起来很好。PS.使启动器static和initVal最有可能成为参数:P@MatthewWatson,它并不是一个真正的工厂方法,因为工厂方法根据定义决定类型,那些工厂方法不决定任何东西,只是简单地构造始终相同的类型。但是,如果命名有效,谁会在乎呢
switch(chosenType){
case ObjectInstance.A:
instance = initiator.CreateObject<A>();
...
A CreateObjectA(InitVal initValue){
return new A(initValue);
}
B CreateObjectB(InitVal initValue){ ...
public class Initiator
{
InitVal initVal;
public A CreateA()
{
return new A(initVal);
}
public B CreateB()
{
return new B(initVal);
}
}
class Initiator
{
readonly Dictionary<Type, Func<MyBase>> _dict = new Dictionary<Type, Func<MyBase>>();
internal Initiator(InitVal initVal)
{
// initialize your "service locator".
// it's cool that different types can have different constructors,
// and people who call CreateObject don't need to know this.
_dict[typeof(A)] = (Func<MyBase>)(() => new A(initVal));
_dict[typeof(B)] = (Func<MyBase>)(() => new B(initVal, someOtherStuff));
}
public T CreateObject<T>() where T : MyBase
{
var ctor = _dict[typeof(T)];
return (T)ctor();
}
}
// this is more likely, you probably don't need a generic method
public IMyBase CreateObject(ObjectInstance objectInstance)
{
// the dictionary would map enum value to Func<IMyBase>, of course
var ctor = _dict[objectInstance];
return ctor();
}
class A : IMyType
{
internal A(InitVal initVal)
}
class B : IMyType
{
internal B(InitVal initVal)
}
class Initiator
{
InitVal initVal = .....;
public T CreateObject<T>() where T : IMyType
{
IMyType myType = null;
if(typeof(T) == typeof(A))
myType = new A(initVal);
else if(typeof(T) == typeof(B))
myType = new B(initVal);
else
throw new MyException("Type is not configured.");
return (T)myType;
}
...
}