Design Patterns · Creational Patterns
Factory Method
Declare a method that creates objects in a base class and let subclasses decide which class to instantiate.
This lesson builds on Program to an interface. Factory Method is also called a virtual constructor.
Factory Method replaces direct constructor calls with a call to a special method. A base class uses that method to get the objects it needs. Subclasses override the method to change which class gets created. The base class never names a concrete class.
The pattern does not remove new. Objects are still constructed with new. The construction just moves into one method that subclasses can override.
Start from code welded to one class
An analytics app exports reports. The first release supports CSV only, so the export code calls new CsvWriter() and uses it directly. The app succeeds, and customers start asking for JSON and Markdown.
The snippets show the creation boundary. Report, the concrete writers, compress(), and upload() stand in for application code. Assume Report.rows contains string[] rows and upload() returns a File.
class ReportExporter {
run(report: Report): File {
const writer = new CsvWriter(); // welded to one format
for (const row of report.rows) writer.row(row);
return upload(compress(writer.finish()));
}
}
Most of the code is about loading, compressing, and uploading. It does not care about the format. But it names CsvWriter, so supporting JSON means a conditional here and in every other place a writer is created. A third format repeats the edit.
Check yourself
ReportExporter.run() calls new CsvWriter() inside a reusable workflow. What couples it to CSV?
Correct. The workflow cannot choose another product without changing or extracting that call.
Not quite. That loop can stay stable if every writer honors the same product contract.
Move construction into an overridable method
Factory Method says: call a factory method instead of the constructor, and declare that method's return type as a shared interface. The objects it returns are called products.
interface ReportWriter {
row(values: string[]): void;
finish(): string;
}
abstract class ExportJob {
// The factory method. Subclasses decide which writer to build.
protected abstract createWriter(): ReportWriter;
// The real job of the class: a workflow that uses the product.
run(report: Report): File {
const writer = this.createWriter();
for (const row of report.rows) writer.row(row);
return upload(compress(writer.finish()));
}
}
class CsvExportJob extends ExportJob {
protected createWriter() {
return new CsvWriter();
}
}
class JsonExportJob extends ExportJob {
protected createWriter() {
return new JsonWriter();
}
}
Subclasses can return different classes only because every product implements ReportWriter, and the factory method promises nothing more specific. The code that calls the factory method, the client code, treats all products the same way.
Despite the name, the creator's main job is not creating
ExportJob owns the export workflow: write, compress, upload. Creating the writer is one step it hands off. A factory method also does not have to return a new object. It can return one from a cache or a pool.
Try it
Run the same workflow with different creators
run() lives in the base class and never changes. Pick the subclass the app instantiates.
job = new CsvExportJob()
job.run(salesReport)
→ this.createWriter() returns a CsvWriter
→ run() writes rows, compresses, uploads
file contents:
region,revenue
north,1200
south,950
job = new JsonExportJob()
job.run(salesReport)
→ this.createWriter() returns a JsonWriter
→ run() writes rows, compresses, uploads
file contents:
[{"region":"north","revenue":1200},
{"region":"south","revenue":950}]
job = new MarkdownExportJob()
job.run(salesReport)
→ this.createWriter() returns a MarkdownWriter
→ run() writes rows, compresses, uploads
file contents:
| region | revenue |
| north | 1200 |
| south | 950 |
Adding Markdown took one creator subclass and one product class. ExportJob.run() was not edited.
Map the roles
| Role | In this example | Job |
|---|---|---|
| Product | ReportWriter | Declares the interface shared by everything the factory method can return |
| Concrete products | CsvWriter, JsonWriter | Different implementations of the product interface |
| Creator | ExportJob | Declares the factory method, returning the product interface, and uses its result. The method can be abstract or return a default product. |
| Concrete creators | CsvExportJob, JsonExportJob | Override the factory method to return a specific product |
Step through
Trace one export
1. The app picks a creator once
Configuration says JSON, so the app builds a JsonExportJob. This is the only place that knows the format.
2. The workflow asks for a product
run() in the base class calls this.createWriter(). It does not know which subclass it is running in.
3. The override builds the concrete product
JsonExportJob.createWriter() returns new JsonWriter().
4. The workflow uses the product through its interface
run() calls row() and finish() on a ReportWriter. Same code for every format.
Reach for it when one of three situations appears
- You do not know ahead of time which classes your code will work with. Factory Method separates the code that constructs products from the code that uses them. Adding a product type means adding a creator subclass.
- You want users of your library or framework to extend its internal components. A web framework's
Routerbuilds aRequestLoggerinternally. Users who want JSON logs need a way in. If the router creates its logger in acreateLogger()factory method, a user subclasses the router, overrides that one method, and returns aJsonRequestLogger. - You want to reuse existing objects instead of rebuilding them. A creation method can return an object from a pool rather than allocate one each time. A plain factory function can also do this. The GoF Factory Method pattern specifically adds an overridable creation step in a creator hierarchy.
Check yourself
Does any helper named create() automatically implement the GoF Factory Method pattern?
Not quite. Intent and collaboration determine the pattern, not the name.
Correct. A plain factory function is useful but does not require the inheritance-based extension point.
Implement it in six steps
- Make all products follow the same interface, with methods that make sense for every product.
- Add an empty factory method to the creator class. Its return type is the product interface.
- Find every product constructor call in the creator. Replace each one with a call to the factory method, moving the construction code into it. A temporary parameter that selects the product type is fine at this stage, even if it means an ugly
switch. - Create a creator subclass for each product type. Override the factory method and move the matching construction code from the base method into it.
- If there are too many product types for one subclass each, reuse a control parameter in the subclasses. An export job that uploads to cloud storage might accept a
formatargument instead of having one subclass per format. - If the base factory method is now empty, make it abstract. If something remains, keep it as the default behavior.
Check yourself
What must be shared before creator subclasses return different writers?
Not quite. Requiring that concrete type prevents unrelated product implementations from substituting.
Correct. Every returned writer must support the operations the stable workflow needs.
Name what it costs
| You gain | You pay |
|---|---|
| No tight coupling between the creator and concrete products | A new subclass for every product type, which can bloat the code |
| Product construction lives in one place (single responsibility) | The pattern fits best when a creator hierarchy already exists. Building one just for this is heavier. |
| New product types without changing client code (open/closed) |
Check yourself
There is one stable writer and no framework extension requirement. What argues against a creator hierarchy?
Correct. A direct constructor or injected factory may be enough until another extension is real.
Not quite. The pattern is a tool, not a required shape for every creation call.
Do not confuse it with its neighbors
| Pattern | Relationship |
|---|---|
| Abstract Factory | Many designs start with Factory Method and evolve toward Abstract Factory, Prototype, or Builder as they need more flexibility. An abstract factory is usually a set of factory methods. |
| Prototype | Prototype creates by cloning a configured object, so it needs no creator subclasses and avoids the costs of inheritance. In exchange, the clone often needs complicated initialization after copying. Factory Method relies on inheritance, but its product needs no separate initialization step. |
| Template Method | Factory Method is a specialization of Template Method: one overridable step whose job is creation. A factory method can also be one step inside a larger template method. |
| Iterator | Collection subclasses can use a factory method to return the iterator type that matches them. |
Check yourself
A factory must create a matching store, queue, and credentials provider for one platform. Which broader intent fits?
Correct. The requirement is a consistent family of products.
Not quite. Copying an existing object does not by itself select and enforce a product family.
Retrieve and apply
Check yourself
Why must the base factory method declare the product interface as its return type, not CsvWriter?
Correct. The base class codes against the interface. Each override can return any class that implements it.
Not quite. Performance is not the point. Substitutability is.
Not quite. You cannot construct an interface. Construction happens in the overrides.
Check yourself
A connection library always returns a new socket from its constructor, and you want to reuse idle sockets. What is the smallest step toward that?
Not quite. That duplicates creation and lifecycle policy. A creation method provides one explicit place for it.
Correct. A creation method can return a pooled object or create a new one. An overridable creator method can use that mechanism, but a simple factory function may be enough.
Not quite. That allows only one socket in total. A pool needs many, reused.
Continue to Abstract Factory to create whole families of products that must match each other.
Source: Alexander Shvets, Dive Into Design Patterns (深入设计模式), Chinese edition v2021-1.25. Factory Method, printed pages 72–85. Explanations, examples, and exercises are adapted for this course.