Compartilhar via


Design Guidelines Update: Factories vs. Constructors

Joe has recently spent some time beefing up the Factory vs. Constructor guidelines. This subject has been the center of lots of intenral debates. Finally, we agreed on the fllowing:

Factories

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. A Factory is an operation or collection of operations that abstract the object creation process for API users, allowing for specialized semantics and finer granularity of control over an object’s instantiation by the API provider. Simply put, a Factory’s primary purpose in life is to generate and provide instances of objects to callers. Factory methods are sometimes logically aggregated into a dedicated Factory type, but just as often appear on the individual classes which are to be created. Such methods are typically static, although instance-based implementations are sometimes necessary to facilitate polymorphic behavior.

Do prefer constructors over Factories as they are generally more consistent and convenient than specialized construction mechanisms. Factories sacrifice discoverability and consistency for implementation flexibility.

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. For example, consider the Singleton, Builder, or other similar patterns that constrain the ways in which objects are created. A constructor is very limited in its ability enforce rich patterns such as these, while a factory method can easily perform caching, throttling, and sharing of objects, for instance.

Consider naming Factory methods by concatenating “Create” and the name of the type being created. For example, consider naming a Factory method that creates buttons CreateButton. In same cases domain specific name can be used, as in File.Open.

Consider naming Factory types by concatenating the name of type being created and “Factory.” For example, consider naming a Factory type that creates Control objects ControlFactory.

Do not implement your Factory using a static method if the construction operation must be available for subclasses to specialize. Using a static method prevents subclasses from the ability to redefine or customize behavior. Use a standard constructor or an instance Factory method for this scenario.

A framework designer is often faced with the difficult choice between using a constructor or a static factory method. Several cases exist in which Factories are the preferred method; in fact, the Framework demonstrates some such scenarios.

For instance, consider the standard Parse method available on the primitive value types.

int i = int.Parse(“35”);

DateTime d = DateTime.Parse(“10/10/1999”);

The semantics of the Parse operation is such that information is converted from one representation of the value into another. In fact, it doesn’t feel like we are constructing a new instance at all, but rather re-hydrating one from existing state (the string). The System.Convert class exposes many such static Factory methods that take a value type in one representation and convert it to an instance of a different value type, retaining the same logical state in the process. Constructors have a very rigid contract with callers: a unique instance of a specific type will be created, initialized and returned. The aforementioned operations are just a few in which this constraint is too strong.

Do use a Factory for conversion style operations.

The use of a Factory also enables conversion specific information to be supplied via parameters without overloading a type’s constructors. This can result in fewer constructors for the API client to choose from and also helps to avoid introducing constructor parameters that are not natural constituents of the type itself, both resulting in an overall less complex type. Conversely, however, constructors are the primary means to create objects, and introducing too many alternatives can be confusing and inconsistent to API users. This is why careful thought and conservatism should go into the creation of any new static Factories. For example, Intellisense will guide a user through the instantiation of a new object using its constructors, but won’t point users in the direction of a type’s Factory methods.

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. Constructors are limited to generating instances of the type to which they belong, but it is often useful to return a subclass to hide implementation details. In many cases, it is a good idea to let users interact with abstract classes and interfaces, so as not to create a dependency on specific implementations. For example, consider this scenario:

public interface IFoo { … }

private class FooImplementer : IFoo { … }

public static IFoo CreateFoo(…) {

    return new FooImplementer(…);

}

The user of the CreateFoo() API does not need to be aware of the presence of the FooImplementer class – in fact, it could even be marked private. Should an alternate implementation become appropriate later on, it is not a breaking change to change the type under the hood:

private class FooImplementerSpecial : IFoo { … }

public static IFoo CreateFoo(…) {

    return new FooImplementerSpecial(…);

}

This provides great flexibility for the API provider in cases where users are looking for a generalized algorithm or type, but isn’t necessarily interested in the implementation details. In such situations, it is important that clients do not take dependencies on the implementation class; otherwise, flexibility to change the implementation at a later date is severely compromised.

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. A Factory method can often use parameters and other context-based information to make this decision for the user.

Often, a collection of related object creation operations are more useful and discoverable when grouped together into a Factory type. The System.IO.File class is one example from the Framework. This type does not need to support polymorphic extension, and as such it implements all of its operations through static methods. For instance, consider the File.Open(…) method:

public class File {

    public static FileStream Open(String path, FileMode mode) {

        return new FileStream(path, mode);

    }

}

//Constructor

FileStream f = new FileStream(“Foo.txt”, FileMode.CreateNew);

//Static Factory Method

FileStream f = File.Create(“Foo.txt”);

This implementation simply constructs a new FileStream, and returns it to the caller. However, the File class is admittedly easier to find than the FileStream type for the average developer (especially one who is not experienced in dealing with the concept of a “Stream”), and provides a convenient directory of file-related operations.

In some cases, Factory types such as these must support polymorphic extension, meaning that Factory operations must be implemented as virtual instance methods. This can be accomplished simply by providing an interface or base class from which concrete Factories derive. For example:

public interface IControlFactory {

    public Control CreateButton();

    public Control CreateListBox();

    …

}

 

public class WindowsFormsControlFactory: IControlFactory{

    …

}

 

public class WebFormsControlFactory: IControlFactory{

    …

}

Consider grouping logically related Factory methods into a single Factory type.

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.

Sometimes using a constructor lacks sufficient context to inform a developer of an operation’s semantics. For example, consider:

public String(char c, int count);

This operation generates a string of count character c’s. Its semantics would have been clearer if a static factory was provided instead, as it provides more guidance through its name. For instance:

public static String Repeat(char c, int count);

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.

Comments

  • Anonymous
    October 11, 2004
    That's really great (really)...thanks!
  • Anonymous
    October 11, 2004
    Krzysztof,

    WM_THX
    thomas woelfer
  • Anonymous
    October 12, 2004
    The comment has been removed
  • Anonymous
    October 12, 2004
    "Do use a Factory if an operation requires parameter information which feels unnatural to pass to a constructor"

    We're doomed. :P

    But really now, this is one of the last phrases I would expect from a Microsoft guideline.
    I know all too many people who have horrible hunches. If you have good hunches, you don't need these guidelines.
    Please consider revising it.
  • Anonymous
    October 14, 2004
    Ugh, thanks for pointing that out Omer. You're entirely correct: we need unambiguous guidance, and that particular sentence is up for way too much interpretation. I'm going to update the wording and will let you know what the result is.

    Thanks for the feedback -- keep it coming!
  • Anonymous
    October 14, 2004
    An update to the Factory vs Constructor design guidelines ...
  • Anonymous
    October 14, 2004
    An update to the Factory vs Constructor design guidelines ...
  • Anonymous
    October 30, 2004
    Interesting Findings this morning
  • Anonymous
    February 06, 2009
    Guid.Parse() and Guid.TryParse() are brilliant. David Hayden didn't say alot.