流畅构建器模式(Fluent Builder pattern)是一种强大的设计模式,它通过更具可读性和可维护性的接口来创建复杂对象。本文将深入探讨如何在 C# 中实现流畅构建器模式,探索基本和高级场景,并分析 .NET 标准库中的实际示例。
在深入实现之前,让我们先了解一下为何要使用流畅构建器模式:
HttpClient
配置在 .NET 中,流畅构建器模式最常见的示例之一就是 HttpClientBuilder
。让我们看看微软是如何实现它的:
var client = new HttpClient(new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(10),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),
MaxConnectionsPerServer = 10
});
虽然这样可以实现功能,但代码不够优雅。以下是我们如何为 HttpClient
实现一个流畅构建器:
public class HttpClientBuilder
{
private readonly SocketsHttpHandler _handler;
private HttpClientBuilder()
{
_handler = new SocketsHttpHandler();
}
public static HttpClientBuilder Create()
{
return new HttpClientBuilder();
}
public HttpClientBuilder WithConnectionLifetime(TimeSpan lifetime)
{
_handler.PooledConnectionLifetime = lifetime;
return this;
}
public HttpClientBuilder WithIdleTimeout(TimeSpan timeout)
{
_handler.PooledConnectionIdleTimeout = timeout;
return this;
}
public HttpClientBuilder WithMaxConnections(int maxConnections)
{
_handler.MaxConnectionsPerServer = maxConnections;
return this;
}
public HttpClient Build()
{
return new HttpClient(_handler);
}
}
现在,我们可以像这样创建一个 HttpClient
:
var client = HttpClientBuilder.Create()
.WithConnectionLifetime(TimeSpan.FromMinutes(10))
.WithIdleTimeout(TimeSpan.FromMinutes(5))
.WithMaxConnections(10)
.Build();
让我们来看一个更复杂的涉及嵌套构建器的场景。假设我们正在为一个 Web 应用程序构建一个配置系统:
public class WebAppConfiguration
{
public DatabaseSettings Database { get; }
public CacheSettings Cache { get; }
public AuthenticationSettings Authentication { get; }
private WebAppConfiguration(WebAppConfigurationBuilder builder)
{
Database = builder.DatabaseBuilder.Build();
Cache = builder.CacheBuilder.Build();
Authentication = builder.AuthenticationBuilder.Build();
}
public class WebAppConfigurationBuilder
{
internal readonly DatabaseSettingsBuilder DatabaseBuilder;
internal readonly CacheSettingsBuilder CacheBuilder;
internal readonly AuthenticationSettingsBuilder AuthenticationBuilder;
public WebAppConfigurationBuilder()
{
DatabaseBuilder = new DatabaseSettingsBuilder(this);
CacheBuilder = new CacheSettingsBuilder(this);
AuthenticationBuilder = new AuthenticationSettingsBuilder(this);
}
public DatabaseSettingsBuilder ConfigureDatabase()
{
return DatabaseBuilder;
}
public CacheSettingsBuilder ConfigureCache()
{
return CacheBuilder;
}
public AuthenticationSettingsBuilder ConfigureAuthentication()
{
return AuthenticationBuilder;
}
public WebAppConfiguration Build()
{
return new WebAppConfiguration(this);
}
}
}
public class DatabaseSettings
{
public string ConnectionString { get; }
public int MaxConnections { get; }
public TimeSpan CommandTimeout { get; }
internal DatabaseSettings(string connectionString, int maxConnections, TimeSpan commandTimeout)
{
ConnectionString = connectionString;
MaxConnections = maxConnections;
CommandTimeout = commandTimeout;
}
}
public class DatabaseSettingsBuilder
{
private readonly WebAppConfiguration.WebAppConfigurationBuilder _parentBuilder;
private string _connectionString;
private int _maxConnections;
private TimeSpan _commandTimeout;
internal DatabaseSettingsBuilder(WebAppConfiguration.WebAppConfigurationBuilder parentBuilder)
{
_parentBuilder = parentBuilder;
}
public DatabaseSettingsBuilder WithConnectionString(string connectionString)
{
_connectionString = connectionString;
return this;
}
public DatabaseSettingsBuilder WithMaxConnections(int maxConnections)
{
_maxConnections = maxConnections;
return this;
}
public DatabaseSettingsBuilder WithCommandTimeout(TimeSpan timeout)
{
_commandTimeout = timeout;
return this;
}
public WebAppConfiguration.WebAppConfigurationBuilder Done()
{
return _parentBuilder;
}
internal DatabaseSettings Build()
{
return new DatabaseSettings(_connectionString, _maxConnections, _commandTimeout);
}
}
public class CacheSettings
{
public string RedisConnection { get; }
public TimeSpan DefaultExpiration { get; }
internal CacheSettings(string redisConnection, TimeSpan defaultExpiration)
{
RedisConnection = redisConnection;
DefaultExpiration = defaultExpiration;
}
}
public class CacheSettingsBuilder
{
private readonly WebAppConfiguration.WebAppConfigurationBuilder _parentBuilder;
private string _redisConnection;
private TimeSpan _defaultExpiration;
internal CacheSettingsBuilder(WebAppConfiguration.WebAppConfigurationBuilder parentBuilder)
{
_parentBuilder = parentBuilder;
}
public CacheSettingsBuilder WithRedisConnection(string redisConnection)
{
_redisConnection = redisConnection;
return this;
}
public CacheSettingsBuilder WithDefaultExpiration(TimeSpan defaultExpiration)
{
_defaultExpiration = defaultExpiration;
return this;
}
public WebAppConfiguration.WebAppConfigurationBuilder Done()
{
return _parentBuilder;
}
internal CacheSettings Build()
{
return new CacheSettings(_redisConnection, _defaultExpiration);
}
}
public class AuthenticationSettings
{
public string JwtSecret { get; }
public TimeSpan TokenExpiration { get; }
internal AuthenticationSettings(string jwtSecret, TimeSpan tokenExpiration)
{
JwtSecret = jwtSecret;
TokenExpiration = tokenExpiration;
}
}
public class AuthenticationSettingsBuilder
{
private readonly WebAppConfiguration.WebAppConfigurationBuilder _parentBuilder;
private string _jwtSecret;
private TimeSpan _tokenExpiration;
internal AuthenticationSettingsBuilder(WebAppConfiguration.WebAppConfigurationBuilder parentBuilder)
{
_parentBuilder = parentBuilder;
}
public AuthenticationSettingsBuilder WithJwtSecret(string jwtSecret)
{
_jwtSecret = jwtSecret;
return this;
}
public AuthenticationSettingsBuilder WithTokenExpiration(TimeSpan tokenExpiration)
{
_tokenExpiration = tokenExpiration;
return this;
}
public WebAppConfiguration.WebAppConfigurationBuilder Done()
{
return _parentBuilder;
}
internal AuthenticationSettings Build()
{
return new AuthenticationSettings(_jwtSecret, _tokenExpiration);
}
}
这使得配置变得非常直观:
var config = new WebAppConfiguration.WebAppConfigurationBuilder()
.ConfigureDatabase()
.WithConnectionString("Server=myserver;Database=mydb")
.WithMaxConnections(100)
.WithCommandTimeout(TimeSpan.FromSeconds(30))
.Done()
.ConfigureCache()
.WithRedisConnection("localhost:6379")
.WithDefaultExpiration(TimeSpan.FromMinutes(10))
.Done()
.ConfigureAuthentication()
.WithJwtSecret("your-secret-key")
.WithTokenExpiration(TimeSpan.FromHours(1))
.Done()
.Build();
StringBuilder
.NET 中的 StringBuilder
类是流畅构建器模式的另一个优秀示例:
var message = new StringBuilder()
.Append("Hello")
.Append(" ")
.Append("World")
.AppendLine("!")
.ToString();
有时,你需要创建适用于泛型类型的构建器。以下是一个泛型集合构建器的示例:
public class CollectionBuilder<T> where T : class
{
private readonly List<T> _items = new List<T>();
private readonly List<Action<List<T>>> _configurations = new List<Action<List<T>>>();
public CollectionBuilder<T> Add(T item)
{
_items.Add(item);
return this;
}
public CollectionBuilder<T> AddRange(IEnumerable<T> items)
{
_items.AddRange(items);
return this;
}
public CollectionBuilder<T> Configure(Action<List<T>> configuration)
{
_configurations.Add(configuration);
return this;
}
public CollectionBuilder<T> WithFilter(Func<T, bool> predicate)
{
_configurations.Add(items =>
{
var itemsToRemove = items.Where(x => !predicate(x)).ToList();
foreach (var item in itemsToRemove)
{
items.Remove(item);
}
});
return this;
}
public IReadOnlyList<T> Build()
{
foreach (var configuration in _configurations)
{
configuration(_items);
}
return _items.AsReadOnly();
}
}
使用示例:
var numbers = new CollectionBuilder<string>()
.Add("1")
.Add("2")
.Add("3")
.AddRange(new[] { "4", "5", "6" })
.Configure(list => list.Sort())
.WithFilter(x => int.Parse(x) > 2)
.Build();
在实现流畅构建器模式时,请考虑以下最佳实践:
Build()
方法。Build()
方法中实现验证。Reset()
方法以便重用构建器。流畅构建器模式在以下情况下最为有用:
流畅构建器模式是你 C# 工具包中的强大工具。如果实现得当,它可以显著提高代码的可读性和可维护性。通过研究 .NET 标准库中的实际示例并理解高级场景,你可以在代码中明智地决定何时以及如何实现该模式。
请记住,虽然该模式很强大,但对于简单对象的创建并非总是必要的。当提高可读性和可维护性的好处超过实现该模式所带来的额外复杂性时,再使用它。