当前位置:  开发笔记 > 编程语言 > 正文

C#中的工厂模式:如何确保对象实例只能由工厂类创建?

如何解决《C#中的工厂模式:如何确保对象实例只能由工厂类创建?》经验,为你挑选了6个好方法。

最近我一直在考虑保护我的一些代码.我很好奇如何确保永远不能直接创建对象,而只能通过工厂类的某种方法创建.让我们说我有一些"业务对象"类,我想确保这个类的任何实例都有一个有效的内部状态.为了实现这一点,我需要在创建对象之前执行一些检查,可能在其构造函数中.这一切都没问题,直到我决定将此检查作为业务逻辑的一部分.那么,我如何安排业务对象只能通过我的业务逻辑类中的某种方法创建,但从不直接?使用C++的旧"朋友"关键字的第一个自然愿望是C#无法实现.所以我们需要其他选择......

让我们试试一些例子:

public MyBusinessObjectClass
{
    public string MyProperty { get; private set; }

    public MyBusinessObjectClass (string myProperty)
    {
        MyProperty = myProperty;
    }
}

public MyBusinessLogicClass
{
    public MyBusinessObjectClass CreateBusinessObject (string myProperty)
    {
        // Perform some check on myProperty

        if (true /* check is okay */)
            return new MyBusinessObjectClass (myProperty);

        return null;
    }
}

这一切都没关系,直到你记得你仍然可以直接创建MyBusinessObjectClass实例,而无需检查输入.我想完全排除这种技术可能性.

那么,社区对此有何看法?



1> Jon Skeet..:

您可以将构造函数设为私有,并将工厂设置为嵌套类型:

public class BusinessObject
{
    private BusinessObject(string property)
    {
    }

    public class Factory
    {
        public static BusinessObject CreateBusinessObject(string property)
        {
            return new BusinessObject(property);
        }
    }
}

这是有效的,因为嵌套类型可以访问其封闭类型的私有成员.我知道这有点限制,但希望它会有所帮助......


@JonSkeet我知道这个问题真的很老了,但我很好奇将`CreateBusinessObject`方法放在嵌套的`Factory`类中,而不是让那个静态方法直接作为`BusinessObject的方法`class ...你能记得你这样做的动机吗?
@KileyNaro:嗯,这就是问题所要求的:)它并不一定带来许多好处,但它回答了这个问题......有时候这很有用 - 建筑师模式会浮现在脑海中.(在这种情况下,构建器将是嵌套类,它将具有一个名为`Build`的*instance*方法.)

2> Ricardo Nold..:

看起来你只想在创建对象之前运行一些业务逻辑 - 那么为什么不在"BusinessClass"中创建一个静态方法来执行所有脏的"myProperty"检查工作,并使构造函数变为私有?

public BusinessClass
{
    public string MyProperty { get; private set; }

    private BusinessClass()
    {
    }

    private BusinessClass(string myProperty)
    {
        MyProperty = myProperty;
    }

    public static BusinessClass CreateObject(string myProperty)
    {
        // Perform some check on myProperty

        if (/* all ok */)
            return new BusinessClass(myProperty);

        return null;
    }
}

调用它将非常简单:

BusinessClass objBusiness = BusinessClass.CreateObject(someProperty);


啊,你也可以抛出异常而不是返回null.
如果您声明了自定义构造函数,那么拥有私有默认构造函数是没有意义的.另外,在这种情况下,使用静态"构造函数"而不是仅仅在真正的构造函数中进行验证(因为它无论如何都是类的一部分)都没有任何好处.它甚至是WORSE,因为你现在可以得到一个空的返回值,为NRE打开并且"这个null到底是什么意思?" 的问题.

3> Fabian Schmi..:

或者,如果你想真正想要的话,反转控制:让班级返回工厂,并用可以创建班级的代表来检测工厂.

public class BusinessObject
{
  public static BusinessObjectFactory GetFactory()
  {
    return new BusinessObjectFactory (p => new BusinessObject (p));
  }

  private BusinessObject(string property)
  {
  }
}

public class BusinessObjectFactory
{
  private Func _ctorCaller;

  public BusinessObjectFactory (Func ctorCaller)
  {
    _ctorCaller = ctorCaller;
  }

  public BusinessObject CreateBusinessObject(string myProperty)
  {
    if (...)
      return _ctorCaller (myProperty);
    else
      return null;
  }
}

:)



4> Matt Hamilto..:

您可以将MyBusinessObjectClass类的构造函数设置为内部,并将它和工厂移动到它们自己的程序集中.现在只有工厂应该能够构造一个类的实例.



5> Fabian Schmi..:

除了Jon建议之外,您还可以将工厂方法(包括检查)放在BusinessObject的静态方法中.然后,将构造函数设置为private,并且其他所有人都将被强制使用静态方法.

public class BusinessObject
{
  public static Create (string myProperty)
  {
    if (...)
      return new BusinessObject (myProperty);
    else
      return null;
  }
}

但真正的问题是 - 为什么你有这个要求?将工厂或工厂方法移到课堂上是否可以接受?



6> Alberto Alon..:

这么多年后,这个问题被问到了,不幸的是,我看到的所有答案都在告诉您应该如何编写代码,而不是给出直接的答案。您正在寻找的实际答案是使用私有构造函数但使用公共实例化程序来创建类,这意味着您只能从其他现有实例中创建新实例...仅在工厂中可用:

您的课程的界面:

public interface FactoryObject
{
    FactoryObject Instantiate();
}

你的班:

public class YourClass : FactoryObject
{
    static YourClass()
    {
        Factory.RegisterType(new YourClass());
    }

    private YourClass() {}

    FactoryObject FactoryObject.Instantiate()
    {
        return new YourClass();
    }
}

最后,工厂:

public static class Factory
{
    private static List knownObjects = new List();

    public static void RegisterType(FactoryObject obj)
    {
        knownObjects.Add(obj);
    }

    public static T Instantiate() where T : FactoryObject
    {
        var knownObject = knownObjects.Where(x => x.GetType() == typeof(T));
        return (T)knownObject.Instantiate();
    }
}

然后,如果您需要用于实例化或预处理您创建的实例的额外参数,则可以轻松修改此代码。由于类构造函数是私有的,因此此代码将允许您通过工厂强制实例化。

推荐阅读
jerry613
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有