Read the full article at : (https://coderlegion.com/17410/abstract-factory-pattern-tutorial)
A real-world implementation of the Abstract Factory Design Pattern using a multi-database access layer that supports:
- Microsoft SQL Server
- MySQL MySQL
- Oracle Corporation Oracle
This project demonstrates how to build a database-agnostic system where the client works only with abstractions and can switch providers at runtime.
Many applications start with code like this:
if(provider == "sqlserver")
{
connection = new SqlConnection(...);
}
else if(provider == "mysql")
{
connection = new MySqlConnection(...);
}
else if(provider == "oracle")
{
connection = new OracleConnection(...);
}This causes:
- Tight coupling
- Repeated conditional logic
- Difficult testing
- Poor scalability
- Open/Closed Principle violations
This project solves that using the Abstract Factory Pattern.
According to the Gang of Four:
"Provide an interface for creating families of related or dependent objects without specifying their concrete classes."
— Design Patterns: Elements of Reusable Object-Oriented Software
IDatabaseFactory
|
------------------------------------------------
| | |
SqlServerFactory MySqlFactory OracleFactory
| | |
Connection Connection Connection
Command Command Command
Reader Reader Reader
Dialect Dialect Dialect
AbstractFactoryDemo/
│
├── Abstractions/
│ ├── IDatabaseFactory.cs
│ ├── IDbConnection.cs
│ ├── IDbCommand.cs
│ ├── IDbDataReader.cs
│ └── IQueryDialect.cs
│
├── SqlServer/
│ ├── SqlServerFactory.cs
│ ├── SqlServerConnection.cs
│ ├── SqlServerCommand.cs
│ ├── SqlServerReader.cs
│ └── SqlServerDialect.cs
│
├── MySql/
│ ├── MySqlFactory.cs
│ ├── MySqlConnection.cs
│ ├── MySqlCommand.cs
│ ├── MySqlReader.cs
│ └── MySqlDialect.cs
│
├── Oracle/
│ ├── OracleFactory.cs
│ ├── OracleConnection.cs
│ ├── OracleCommand.cs
│ ├── OracleReader.cs
│ └── OracleDialect.cs
│
├── Client/
│ └── DatabaseClient.cs
│
└── Program.csEach factory creates a family of compatible objects:
| Factory | Creates |
|---|---|
| SqlServerFactory | SQL Server objects |
| MySqlFactory | MySQL objects |
| OracleFactory | Oracle objects |
The client never directly creates concrete implementations.
public interface IDatabaseFactory
{
string ProviderName { get; }
IQueryDialect Dialect { get; }
IDbConnection CreateConnection(string connectionString);
IDbCommand CreateCommand(
string sql,
IDbConnection connection);
IDbDataReader CreateDataReader(
IDbCommand command);
}Different databases use different SQL syntax.
public interface IQueryDialect
{
string ApplyLimit(string sql, int limit);
string FormatParameter(string name);
string BuildConnectionString(
string host,
string database,
string? user = null,
string? password = null);
}The client works only with abstractions.
public class DatabaseClient
{
private readonly IDatabaseFactory _factory;
public DatabaseClient(IDatabaseFactory factory)
{
_factory = factory;
}
}Clone the repository:
git clone https://github.com/yourusername/AbstractFactoryDemo.git
cd AbstractFactoryDemoRun the application:
dotnet runThe application asks the user which database to use:
Choose a database provider:
1. SQL Server
2. MySQL
3. Oracle
Example:
switch(choice)
{
case "1":
factory = new SqlServerFactory();
break;
case "2":
factory = new MySqlFactory();
break;
case "3":
factory = new OracleFactory();
break;
}This is acceptable because it happens at the composition root.
await client.RunQueryAsync(...);
await client.RunNonQueryAsync(...);
await client.RunScalarAsync(...);==============================================
Abstract Factory Pattern — Database Demo
==============================================
Choose a database provider:
1. SQL Server
2. MySQL
3. Oracle
Enter your choice: 2
Using MySQL...
Running operations...
--- RunQuery via MySQL ---
Row: Id=1 Name=Alice
--- RunNonQuery via MySQL ---
Rows affected: 1
--- RunScalar via MySQL ---
Scalar result: 15
Want to support PostgreSQL Global Development Group?
Add:
- PostgreSqlFactory
- PostgreSqlConnection
- PostgreSqlCommand
- PostgreSqlReader
- PostgreSqlDialect
No changes needed in:
DatabaseClient- Business logic
- Existing factories
- ✅ Open/Closed Principle
- ✅ Easy testing
- ✅ Scalable architecture
- ✅ Clean separation of concerns
- ✅ Runtime flexibility
Avoid this pattern when:
- You support only one database
- You don’t have multiple object families
- A simple factory method is enough
- Design Patterns: Elements of Reusable Object-Oriented Software
- Clean Architecture by Robert C. Martin
Potential enhancements:
- Dependency Injection version
- Unit tests with fake factories
- Docker database containers
- PostgreSQL implementation
- Entity Framework version
Pull requests are welcome.
For major changes:
- Fork the repo
- Create a feature branch
- Submit a PR
MIT License