我应该使用什么样的创作模式?

时间:2016-09-13 07:20:52

标签: c# design-patterns creation-pattern

我的课程有两节课;两者都来自相同的基类。

class A : MyBase
{
    internal A(InitVal initVal)
}

class B : MyBase
{
    internal B(InitVal initVal)
}

InitVal是另一个通过构造函数注入的类。此课程供内部使用。由于内部构造函数,用户无法直接创建类AB的实例。相反,我创建了创建这些对象的方法。

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;
    }
    ...
}

ObjectInstance在上面的代码中是枚举的。

这没有问题,但我相信你以前从未见过如此丑陋的代码。

请建议我应该使用的创作模式。我想在不更改功能的情况下删除ObjectInstance枚举。它会清理很多。

我尝试了dotfactory上提到的 Creational Patterns 。 在这种情况下,Factory MethodAbstract Factory看起来不合适。

我的代码虽然看起来很难看,但阅读和理解起来非常简单。我尝试实现上面提到的模式,这增加了代码复杂性所以这也是我选择答案时的标准。

除了Initiator类之外,我无法更改代码中的任何内容。我无法访问所有其他课程进行编辑。

编辑1:为什么上面的代码在我看来很难看

1)在调用CreateObject方法时,用户必须指定对象的类型两次。

A a = initiator.CreateObject<A>(ObjectInstance.A);

首先是T通用值,第二个是枚举值。 我想避免这种情况。

2)由于用户必须指定对象类型两次,否则可能会出错。

A a = initiator.CreateObject<A>(ObjectInstance.B);

在上面的代码中,枚举值和通用值是不同的。 这是不允许的,这将是一个问题。 用我的代码,我无法避免这种情况。

这就是原因;我正在寻找适合我案例的模式,而不会增加复杂性。

如果我以某种方式删除枚举的必要性,代码会好很多。 如果我可以将CreateObject的签名更改为以下内容,那就更好了。

public T CreateObject<T>() where T : MyBase

但是,我不确定如何实现此方法来创建适当的实例。

4 个答案:

答案 0 :(得分:2)

我并不认为你在尝试制作这种通用产品方面有任何优势。您需要知道呼叫站点返回值的具体类型。

因此,为什么不保持简单而只是这样做?

public class Initiator
{
    InitVal initVal;

    public A CreateA()
    {
        return new A(initVal);
    }

    public B CreateB()
    {
        return new B(initVal);
    }
}

答案 1 :(得分:1)

当你将方法指定为通用方法时,我希望你可能真的知道你想在编译时得到的类型..所以我会选择这样的东西:

class Initiator
{ 
    public T CreateObject<T>(ObjectInstance objectInstance) where T : MyBase, new()
    {
        T newInstance = new T();
        newInstance.Value = initVal;

        return newInstance;
    }
...
}

现在您可以将其命名为:

A myAInstance = initiator.CreateObject<A>();
MyBase myAInstance = initiator.CreateObject<A>();   //this also works

要使其工作,您需要在类中指定内部无参数构造函数,并为Value属性或当前在当前构造函数中设置的任何内容指定 interface

class MyBase{
    InitVal Value { get; set;}       //this allows construction of the object with parameterless constructor
    ...
}

这不仅更干净,更短,而且更不容易出错,因为每次添加新类型时都不需要编辑枚举和方法体。但是,它为子类型特定逻辑提供了较少的灵活性。

注意:如果你真的想拥有带有参数的构造函数,你仍然可以采用这种方法,但是你需要使用反射(检查Activator)或lambdas。

当然,如果您可以在编译期间决定类型,或者您只想将此分区委托给第三方库,那么这只是有意义的,例如:

switch(chosenType){
case ObjectInstance.A:
    instance = initiator.CreateObject<A>();
    ...

否则,只需保持原样,或多或少的FactoryMethod模式,它就可以完成工作。只是它中的通用参数......似乎没那么无用。我会删除它并将返回类型更改为MyBase,因为用户无论如何都无法指定T.

最后一个选择是简单地为每种类型创建一个单独的方法,这是干净,灵活的,提供了很多自定义选项,但是如果你需要重复很多共享逻辑那么很糟糕并且您需要为每个下一个类型添加一个新的。简单地:

A CreateObjectA(InitVal initValue){
     return new A(initValue);
}
B CreateObjectB(InitVal initValue){ ...

答案 2 :(得分:0)

代码的一个明显问题是枚举,这是不必要的,因为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();
    }
}

或者,如果您不知道类型,则可以传递枚举,但返回类型应该是接口/基类(最好是接口):

// 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();
}

现在你有一个简单的“穷人”&#34; DI类叫Initiator,所以我想知道你的DI框架(注入InitVal的框架)是否也可以注入AB个实例。这可能是真的,因为DI纯粹主义者会告诉你工厂没有地方,代码中有new关键字。

顺便说一下,ObjectInstance是一个非常非常糟糕的枚举名称。

答案 3 :(得分:0)

我是按照以下方式做到的:

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;
    }
    ...
}

这解决了我在问题中提到的问题。但是,它会产生新的问题。 这违反了SOLID的开放原则。最后else块处理手动错误(如果有的话)。无论如何,它只适用于我的具体情况; 一般不推荐