The factory pattern has many uses. I'll give brief description of them and their uses with very simple examples.
NOTE: The examples have only illustrational purpose, so some of them may seem like an overkill other may not seem like a good solution to the problem. Giving realistic example for everything is hard, so I'll give myself some freedom here by ignoring some principles for illustrational purposes
Problem: You have a class that has many constructors with different parameters. This makes the code hard to read, write and reason about.
Solution: Create a factory that simplifies the creation of objects and makes the code cleaner by having multiple well named methods.
Example: You have a system that has many different types of products.
public enum ProductType { Book, Paper, Pen }
public class Product {
public ProductType Type { get; set;}
}
public class ProductFactory {
public Product CreateBook() { return new Product(ProductType.Book); }
public Product CreatePaper() { return new Product(ProductType.Paper); }
public Product CreatePen() { return new Product(ProductType.Pen); }
}
Problem: You have an object that is created from different input and/or by having to process this input. Or you have an object that has complex creational process.
Solution: Create a factory that does this for you with well named methods.
Example: You have a system that processes files from the file system and uses their extensions for complex operations. You want to use FileExtension class
to handle equality (in Windows is case insensitive) and other operations so you can avoid having floating strings around , avoid having bugs from case insensitive comparison and make maximum use of strong typing
public class FileExtension { }
public class FileExtensionFactory {
public FileExtension Create(string extension) {
return new FileExtension(extension);
}
public FileExtension FromPath(string fullPath) {
return new FilePath(Path.GetExtension(fullPath);
}
}
public class SomeClassThatUsesTheFactory {
public void DoSomethingWithFileExtension(string filePath) {
var extension = FileExtensionFactory.FromPath(filePath);
// do something with the extension
}
}
Here you can see that the usage of the FromPath
method makes the code cleaner and more readable. It also hides the way that filePath
parameter is parsed and avoids duplication of the parsing code every time you need to create an extension from filePath
.
Problem: You need to create instances of a family of related objects that plug-in to your system.
Solution: Use abstract factory design pattern.
Example: You are building an ORM and you want to support different types of databases. You want to define an interface for database connections that your ORM code will use. Later you want to be able to setup your ORM with different types of connections so you can connect to different databases. You want to be able to add support for more databases to your ORM.
public interface IDbCommand : IDiposable {
string CommandText { get; set; }
void Execute();
}
public interface IDbConnection : IDisposable {
void Open();
void Close();
IDbCommand CreateCommand();
IDbTransaction BeginTransaction();
// other stuff
}
public interface IDbConnectionFactory {
IDbConnection CreateConnection();
}
public class MySqlCommand : IDbCommand { }
public class MySqlConnection : IDbConnection { }
public class MySqlConnectionFactory : IDbConnectionFactory { }
public class PostgreCommand : IDbCommand { }
public class PostgreConnection : IDbConnection { }
public class PostgreSqlConnectionFactory : IDbConnectionFactory { }
public class OrmSession {
public OrmSession(IDbConnectionFactory) { }
}
Here we see the Open-Closed principle in action. You ORM will use the abstract factory, so you can provide a concrete factory. You can do this from your code, use a configuration file or use ServiceLocator or DI to inject a concrete factory.
Notice also something interesting here. IDbConnection
also provides a CreateCommand
FactoryMethod. Each connection will need to create a concrete command, so it provides a method for this even if it's not a pure factory.
There are other situations in which you can use factories. Testing if one of them. You may need to provide an Abstract Factory so you can make parts of your app testable.
As a conclusion, the Factory pattern has many uses. They can vary from just making the code cleaner and more expressive to achieving the Open-Closed principle. The driving force here is the problem that you have. You have problems with object creation. Can a factory solve your problem?
Not always. Sometimes the creation of object is so complex and has many different variations that a Factory doesn't help and can make things messy. Sometimes a constructor or two is all you need.
In case of complex objection creation, you can use the BuilderPattern with or without FluentInterface.