开发者

Factory vs instance constructors

开发者 https://www.devze.com 2023-01-01 18:54 出处:网络
I can\'t think of any reasons why one is better than the other.Compare these two implementations: public class MyClass

I can't think of any reasons why one is better than the other. Compare these two implementations:

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

as opposed to:

public class MyClass
{
    private MyClass()开发者_Go百科{}

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

There are some places in the .Net framework that use a static method to create instances. At first I was thinking, it registers it's instances to keep track of them, but regular constructors could do the same thing through the use of private static variables.

What is the reasoning behind this style?


Note: What you have is not a static constructor, it's a static function that creates the instance rather than calling the instance constructor yourself. A static constructor is a different thing entirely.

The factory pattern is a classic example of using a function (static or not) to instantiate a type rather than using the constructor directly. Note that the actual instance constructor will get called no matter what, but the static function provides a layer of indirection that allows it to return an instance of any type that either is or inherits from the return type, rather than only instances that are the return type.

For example:

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

This allows you to hide Class1 and Class2 from external assemblies while still allowing the consumer to deal with something specialized.


I was researching this very point and came across this question but didn't feel it had been answered fully. However, I did find this handy article - Design Guidelines Update: Factories vs. Constructors - by Krzysztof Cwalina (a a Principal Architect on the .NET Framework). It is worth reading the entire article, but here is a brief summary of the main points:

The most common and consistent way to create an instance of a type is via its constructor. However, sometimes a preferable alternative is to use the Factory pattern.

Do prefer constructors over Factories as they are generally more consistent and convenient than specialized construction mechanisms.

Do implement Factory operations as methods, not properties.

Do return instances as method return values, not as out parameters.

Consider using a Factory if you need more control over the creation patterns of instances.

Consider naming Factory methods by concatenating “Create” and the name of the type being created.

Consider naming Factory types by concatenating the name of type being created and “Factory.”

Do not implement your Factory using a static method if the construction operation must be available for subclasses to specialize.

Do use a Factory for conversion style operations.

Do use a Factory if an operation requires parameter information which feels unnatural to pass to a constructor.

Do not use a Factory in situations where the creation operation will be used as a core scenario for instantiation of a type. In these situations, discoverability and consistency are paramount.

Consider using a constructor instead of a Factory. Only after this thought process should you proceed with the implementation of a Factory.

Factories are also often handy to enable type specialization through polymorphism.

Do use a Factory if API users will be coding to a base class or interface whose implementation is subject to change over time.

Do use Factory methods in cases where a developer might not know which type to construct, for instance when coding against a base type or interface.

Do implement Factory operations as virtual instance methods rather than static if they must support polymorphic extension.

Do use the Singleton pattern for Factories which are instance based so as not to force developers to instantiate a Factory type just to invoke one of its members.

Do use a Factory method if a constructor would be insufficient to describe the operation being performed, and the additional information gained from an individually named Factory makes an operation’s purpose clearer.


Another common case where factory methods help (in languages like C# which don't support type inference from constructor) is when you have to minimize type parameter specification. Consider the common case of Tuple class. You can either do:

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

or

Tuple.Create(1, 1); //factory pattern.


The static Create method can instantiate and return:

  • A MyClass instance
  • An instance of any subclass of MyClass
  • null


The factory method is often used to hide the exact type of the object being created. An abstract class can have a static factory method, which then selects the concrete derived class to instantiate depending on some condition. Only the abstract base class is publicly documented. This leaves the library supplier some freedom to change the exact type being instantiated without breaking existing code.

An example from the .NET framework is the static factory methods on the Expression class.


Sometimes it is important to do bookkeeping and/or resource management for every instance of a class created. As a result, it is important that each construction is managed globally and a static method to do the construction will do that nicely.


As you can see, myClass is not following a "classic" factory pattern (where the class of the instance is not known/exposed outside the factory).

However in this case, the .NET framework team may not be aiming for a factory pattern, but I guess they do not want you to just new up the target class directly with a filename via a constructor. A factory might be overkill if only this class is being provided.

This pattern is sometimes seen in clone() methods, where an object can return an instance which is a kind of duplicate of itself.

Also maybe although the class is public, they might want to do some checks with the instantiation, filename, etc. and if they implemented a factory, the target class could still be created and invoked, bypassing the checks.


Methods that do the construction for you are super useful for:

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

...since "normal" constructors cannot do this.


Not spilling out dependency to assemblies referring your assembly is another reason.

It seems like if calling a constructor for a class in another assembly, your assembly will be required to references all the assemblies that defines any of the types used by any overload. You can workaround this forced dependency by using differently named factory methods.

Example:

Assembly1.dll (requires ref to BarAssembly.dll not so obvious)

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

Assembly2.dll (no need for ref to BarAssembly.dll here, as CreateFoo and CreateFooWithBar is not overloads)

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

FooAssembly.dll (requires ref to BarAssembly.dll obvious)

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

BarAssembly.dll

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

Note: Observed on VS2013 building against .NET Framework 4.5


You can curry factory methods. Trying to curry a constructor gives you: a factory method.

I use this in some of my code that enumerates the Windows Device Manager tree. One of the classes lists the serial ports, each serial port has a connection factory property which returns a curried factory method with the port address (really ugly PNP device string) stored in internal state while the baud rate, stop bits, parity are all provided later.

0

精彩评论

暂无评论...
验证码 换一张
取 消