代码之家  ›  专栏  ›  技术社区  ›  Neil N HLGEM

工厂与实例构造函数

  •  36
  • Neil N HLGEM  · 技术社区  · 16 年前

    我想不出任何理由为什么一个比另一个好。比较这两种实现:

    public class MyClass
    {
        public MyClass(string fileName)
        {
            // some code...
        }
    }
    

    与之相反:

    public class MyClass
    {
        private MyClass(){}
    
        public static MyClass Create(string fileName)
        {
           // some code...
        }
    }
    

    .NET框架中有一些地方使用静态方法创建实例。起初我在想,它注册实例来跟踪它们,但是常规构造函数可以通过使用私有静态变量来做同样的事情。

    这种风格背后的原因是什么?

    10 回复  |  直到 7 年前
        1
  •  49
  •   hdoghmen Neil Knight    11 年前

    注:您拥有的是 一个静态构造函数,它是一个创建实例而不是自己调用实例构造函数的静态函数。静态构造函数完全不同。

    工厂模式是使用函数(静态或非静态)实例化类型的经典示例,而不是直接使用构造函数。请注意,实际的实例构造函数将被调用,不管是什么,但是静态函数提供了一个间接层,允许它返回任何类型的实例,这些实例要么是返回类型,要么是从返回类型继承的,而不仅仅是那些 返回类型。

    例如:

    public abstract class BaseClass
    {
        public static BaseClass Create(int parameter)
        {
            if (parameter == 1)
            {
                return new Class1();
            }
            else
            {
                return new Class2();
            }
        }
    }
    
    internal class Class1 : BaseClass
    {
        //code here ...
    }
    
    internal class Class2 : BaseClass
    {
        //code here ...
    }
    

    这可以让你隐藏 Class1 Class2 从外部组件,同时仍然允许消费者处理一些专门的事情。

        2
  •  19
  •   Dan Diplo    14 年前

    我正在研究这一点,遇到了这个问题,但感觉没有得到充分的回答。不过,我确实找到了这篇方便的文章- Design Guidelines Update: Factories vs. Constructors -作者KrzysztofCwalina(一个.NET框架的主要架构师)。值得一读整篇文章,但这里简要总结了以下要点:

    创建类型实例的最常见和一致的方法是 通过其构造函数。然而,有时一个更好的选择是 使用工厂模式。

    比工厂更喜欢建筑工人,因为他们通常更喜欢工厂。 比专业化的施工机制一致、方便。

    不要将工厂操作实现为方法,而不是属性。

    请将实例作为方法返回值而不是作为输出参数返回。

    如果需要对创建进行更多控制,请考虑使用工厂 实例模式。

    考虑通过连接_156;create_157;和名称来命名工厂方法 正在创建的类型。

    考虑通过连接类型的名称来命名工厂类型 创建并_工厂。__

    如果 构造操作必须对要专门化的子类可用。

    使用工厂进行转换样式操作。

    如果操作需要以下参数信息,请使用工厂: 传递给构造函数感觉不自然。

    在创建操作将要执行的情况下,不要使用工厂 用作类型实例化的核心方案。在这些 情况、可发现性和一致性是最重要的。

    考虑使用构造函数而不是工厂。仅此之后 思考过程应该继续执行 工厂。

    工厂通常也很方便通过 多态性。

    如果API用户要编码到基类或 其实现随时间变化的接口。

    在开发人员可能不知道的情况下使用工厂方法 要构造的类型,例如,对基类型或 接口。

    将工厂操作作为虚拟实例方法来实现,而不是 如果它们必须支持多态扩展,则返回static。

    对基于实例的工厂使用单例模式,因此 为了不强制开发人员实例化工厂类型 调用其成员之一。

    如果构造函数不足以 描述正在执行的操作以及附加信息 从一个单独命名的工厂中获得的信息可以使操作达到目的。 更清楚。

        3
  •  9
  •   Community Mohan Dere    9 年前

    工厂方法有帮助的另一个常见情况(在C语言中# which don't support type inference from constructor )当必须最小化类型参数规范时。考虑以下常见情况: Tuple 班级。您可以这样做:

    new Tuple<int, int>(1, 1); //constructor call
    

    Tuple.Create(1, 1); //factory pattern.
    
        4
  •  7
  •   ChrisW    16 年前

    静态 Create 方法可以实例化并返回:

    • MyClass 实例
    • 任何实例 子类 属于 类名
    • null
        5
  •  3
  •   Anders Abel    16 年前

    工厂方法通常用于隐藏正在创建的对象的确切类型。抽象类可以有一个静态工厂方法,然后根据某些条件选择要实例化的具体派生类。只有抽象基类是公开文档。这使得库供应商可以在不破坏现有代码的情况下自由地更改正在实例化的确切类型。

    .NET框架中的一个示例是 Expression 班级。

        6
  •  1
  •   plinth    16 年前

    有时,对创建的类的每个实例进行簿记和/或资源管理很重要。因此,重要的是要对每一个构建进行全局管理,而一个静态的构建方法将很好地做到这一点。

        7
  •  1
  •   Ra.    16 年前

    如您所见,myClass没有遵循“经典”工厂模式(在工厂外实例的类是未知的/公开的)。

    然而,在这种情况下,.NET框架团队可能不会瞄准工厂模式,但我想他们不希望您通过构造函数直接用文件名来创建目标类。 如果只提供此类,工厂可能会被过度杀戮。

    在clone()方法中有时会看到这种模式,对象可以返回一个与自身类似的实例。

    另外,尽管该类是公共的,但他们可能希望对实例化、文件名等进行一些检查。如果他们实现了工厂,则仍然可以创建和调用目标类,而忽略这些检查。

        8
  •  1
  •   Tobias Valinski    12 年前

    为您构造的方法非常有用:

    public class Class
        {
            public static List<Class> FromFiles(IEnumerable<String> paths)
            {
                List<Class> instances = new List<Class>();
                foreach (string path in paths)
                {
                    instances.Add(new Class() { Path = path });
                }
                return instances;
            }
    
            public string Path { get; set; }
        }
    

    …因为“普通”构造函数不能这样做。

        9
  •  1
  •   Daniel    7 年前

    不泄漏对引用程序集的程序集的依赖性是另一个原因。

    似乎如果在另一个程序集中为某个类调用构造函数,您的程序集将需要引用定义任何重载所使用的任何类型的所有程序集。您可以通过使用不同名称的工厂方法来解决这种强制依赖。

    例子:

    assembly1.dll(要求对barassembly.dll的引用不太明显)

    class Class1 { 
        void Main(){
            var foo = new Foo();
        }
    }
    

    assembly2.dll(此处不需要引用barassembly.dll,因为createfoo和createfoowithbar不是重载)

    class Class2 { 
        void Main(){
            var foo = CreateFoo();
        }
    }
    

    fooassembly.dll(需要对barassembly.dll的明显引用)

    class Foo { 
        public CreateFoo(){
            ...
        }
        public CreateFooWithBar(Bar bar){
            ...
        }
        public Foo(){
            ...
        }
        public Foo(Bar bar){
            ...
        }
    }
    

    数据库汇编语言

    class Bar { 
        public Bar(){
            ...
        }
    }
    

    注:根据.NET Framework 4.5在VS2013建筑物上观察

        10
  •  0
  •   Ben Voigt    15 年前

    你可以用咖喱工厂的方法。尝试使用构造函数会给您一个工厂方法。

    我在列举Windows设备管理器树的一些代码中使用了这个。其中一个类列出了串行端口,每个串行端口都有一个连接工厂属性,该属性返回一个以内部状态存储的端口地址(非常难看的PNP设备字符串)为基础的工厂方法,而波特率、停止位、奇偶校验都是稍后提供的。