掌握 C# 中的 Fluent Builder 模式:从基础到高级方案
流畅构建器模式(Fluent Builder pattern)是一种强大的设计模式,它通过更具可读性和可维护性的接口来创建复杂对象。本文将深入探讨如何在 C# 中实现流畅构建器模式,探索基本和高级场景,并分析 .NET 标准库中的实际示例。
为何使用流畅构建器模式?
在深入实现之前,让我们先了解一下为何要使用流畅构建器模式:
- 通过方法链提高代码的可读性。
- 将复杂对象的构建过程与其表示形式分离。
- 在保持对象灵活构建的同时,强制实现不可变性。
- 优雅地处理可选参数。
- 为对象构建提供清晰的 API。
实际示例:HttpClient
配置
在 .NET 中,流畅构建器模式最常见的示例之一就是 HttpClientBuilder
。让我们看看微软是如何实现它的:
var client = new HttpClient(new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(),
MaxConnectionsPerServer =
});
虽然这样可以实现功能,但代码不够优雅。以下是我们如何为 HttpClient
实现一个流畅构建器:
public classHttpClientBuilder
{
privatereadonlySocketsHttpHandler _handler;
privateHttpClientBuilder()
{
_handler =newSocketsHttpHandler();
}
publicstaticHttpClientBuilderCreate()
{
returnnewHttpClientBuilder();
}
publicHttpClientBuilderWithConnectionLifetime(TimeSpan lifetime)
{
_handler.PooledConnectionLifetime = lifetime;
returnthis;
}
publicHttpClientBuilderWithIdleTimeout(TimeSpan timeout)
{
_handler.PooledConnectionIdleTimeout = timeout;
returnthis;
}
publicHttpClientBuilderWithMaxConnections(int maxConnections)
{
_handler.MaxConnectionsPerServer = maxConnections;
returnthis;
}
publicHttpClientBuild()
{
returnnewHttpClient(_handler);
}
}
现在,我们可以像这样创建一个 HttpClient
:
var client = HttpClientBuilder.Create()
.WithConnectionLifetime(TimeSpan.FromMinutes())
.WithIdleTimeout(TimeSpan.FromMinutes())
.WithMaxConnections()
.Build();
高级场景:带有配置上下文的嵌套构建器
让我们来看一个更复杂的涉及嵌套构建器的场景。假设我们正在为一个 Web 应用程序构建一个配置系统:
代码语言:javascript代码运行次数:0运行复制public classWebAppConfiguration
{
publicDatabaseSettings Database {get;}
publicCacheSettings Cache {get;}
publicAuthenticationSettings Authentication {get;}
privateWebAppConfiguration(WebAppConfigurationBuilder builder)
{
Database = builder.DatabaseBuilder.Build();
Cache = builder.CacheBuilder.Build();
Authentication = builder.AuthenticationBuilder.Build();
}
publicclassWebAppConfigurationBuilder
{
internalreadonlyDatabaseSettingsBuilder DatabaseBuilder;
internalreadonlyCacheSettingsBuilder CacheBuilder;
internalreadonlyAuthenticationSettingsBuilder AuthenticationBuilder;
publicWebAppConfigurationBuilder()
{
DatabaseBuilder =newDatabaseSettingsBuilder(this);
CacheBuilder =newCacheSettingsBuilder(this);
AuthenticationBuilder =newAuthenticationSettingsBuilder(this);
}
publicDatabaseSettingsBuilderConfigureDatabase()
{
return DatabaseBuilder;
}
publicCacheSettingsBuilderConfigureCache()
{
return CacheBuilder;
}
publicAuthenticationSettingsBuilderConfigureAuthentication()
{
return AuthenticationBuilder;
}
publicWebAppConfigurationBuild()
{
returnnewWebAppConfiguration(this);
}
}
}
publicclassDatabaseSettings
{
publicstring ConnectionString {get;}
publicint MaxConnections {get;}
publicTimeSpan CommandTimeout {get;}
internalDatabaseSettings(string connectionString,int maxConnections,TimeSpan commandTimeout)
{
ConnectionString = connectionString;
MaxConnections = maxConnections;
CommandTimeout = commandTimeout;
}
}
publicclassDatabaseSettingsBuilder
{
privatereadonlyWebAppConfiguration.WebAppConfigurationBuilder _parentBuilder;
privatestring _connectionString;
privateint _maxConnections;
privateTimeSpan _commandTimeout;
internalDatabaseSettingsBuilder(WebAppConfiguration.WebAppConfigurationBuilder parentBuilder)
{
_parentBuilder = parentBuilder;
}
publicDatabaseSettingsBuilderWithConnectionString(string connectionString)
{
_connectionString = connectionString;
returnthis;
}
publicDatabaseSettingsBuilderWithMaxConnections(int maxConnections)
{
_maxConnections = maxConnections;
returnthis;
}
publicDatabaseSettingsBuilderWithCommandTimeout(TimeSpan timeout)
{
_commandTimeout = timeout;
returnthis;
}
publicWebAppConfiguration.WebAppConfigurationBuilderDone()
{
return _parentBuilder;
}
internalDatabaseSettingsBuild()
{
returnnewDatabaseSettings(_connectionString, _maxConnections, _commandTimeout);
}
}
publicclassCacheSettings
{
publicstring RedisConnection {get;}
publicTimeSpan DefaultExpiration {get;}
internalCacheSettings(string redisConnection,TimeSpan defaultExpiration)
{
RedisConnection = redisConnection;
DefaultExpiration = defaultExpiration;
}
}
publicclassCacheSettingsBuilder
{
privatereadonlyWebAppConfiguration.WebAppConfigurationBuilder _parentBuilder;
privatestring _redisConnection;
privateTimeSpan _defaultExpiration;
internalCacheSettingsBuilder(WebAppConfiguration.WebAppConfigurationBuilder parentBuilder)
{
_parentBuilder = parentBuilder;
}
publicCacheSettingsBuilderWithRedisConnection(string redisConnection)
{
_redisConnection = redisConnection;
returnthis;
}
publicCacheSettingsBuilderWithDefaultExpiration(TimeSpan defaultExpiration)
{
_defaultExpiration = defaultExpiration;
returnthis;
}
publicWebAppConfiguration.WebAppConfigurationBuilderDone()
{
return _parentBuilder;
}
internalCacheSettingsBuild()
{
returnnewCacheSettings(_redisConnection, _defaultExpiration);
}
}
publicclassAuthenticationSettings
{
publicstring JwtSecret {get;}
publicTimeSpan TokenExpiration {get;}
internalAuthenticationSettings(string jwtSecret,TimeSpan tokenExpiration)
{
JwtSecret = jwtSecret;
TokenExpiration = tokenExpiration;
}
}
publicclassAuthenticationSettingsBuilder
{
privatereadonlyWebAppConfiguration.WebAppConfigurationBuilder _parentBuilder;
privatestring _jwtSecret;
privateTimeSpan _tokenExpiration;
internalAuthenticationSettingsBuilder(WebAppConfiguration.WebAppConfigurationBuilder parentBuilder)
{
_parentBuilder = parentBuilder;
}
publicAuthenticationSettingsBuilderWithJwtSecret(string jwtSecret)
{
_jwtSecret = jwtSecret;
returnthis;
}
publicAuthenticationSettingsBuilderWithTokenExpiration(TimeSpan tokenExpiration)
{
_tokenExpiration = tokenExpiration;
returnthis;
}
publicWebAppConfiguration.WebAppConfigurationBuilderDone()
{
return _parentBuilder;
}
internalAuthenticationSettingsBuild()
{
returnnewAuthenticationSettings(_jwtSecret, _tokenExpiration);
}
}
这使得配置变得非常直观:
代码语言:javascript代码运行次数:0运行复制var config =newWebAppConfiguration.WebAppConfigurationBuilder()
.ConfigureDatabase()
.WithConnectionString("Server=myserver;Database=mydb")
.WithMaxConnections()
.WithCommandTimeout(TimeSpan.FromSeconds())
.Done()
.ConfigureCache()
.WithRedisConnection("localhost:6379")
.WithDefaultExpiration(TimeSpan.FromMinutes())
.Done()
.ConfigureAuthentication()
.WithJwtSecret("your-secret-key")
.WithTokenExpiration(TimeSpan.FromHours())
.Done()
.Build();
实际示例:StringBuilder
.NET 中的 StringBuilder
类是流畅构建器模式的另一个优秀示例:
var message = new StringBuilder()
.Append("Hello")
.Append(" ")
.Append("World")
.AppendLine("!")
.ToString();
高级模式:带约束的泛型构建器
有时,你需要创建适用于泛型类型的构建器。以下是一个泛型集合构建器的示例:
代码语言:javascript代码运行次数:0运行复制public classCollectionBuilder<T>whereT:class
{
privatereadonlyList<T> _items =newList<T>();
privatereadonlyList<Action<List<T>>> _configurations =newList<Action<List<T>>>();
publicCollectionBuilder<T>Add(T item)
{
_items.Add(item);
returnthis;
}
publicCollectionBuilder<T>AddRange(IEnumerable<T> items)
{
_items.AddRange(items);
returnthis;
}
publicCollectionBuilder<T>Configure(Action<List<T>> configuration)
{
_configurations.Add(configuration);
returnthis;
}
publicCollectionBuilder<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);
}
});
returnthis;
}
publicIReadOnlyList<T>Build()
{
foreach(var configuration in _configurations)
{
configuration(_items);
}
return _items.AsReadOnly();
}
}
使用示例:
代码语言:javascript代码运行次数:0运行复制var numbers =newCollectionBuilder<string>()
.Add("1")
.Add("2")
.Add("3")
.AddRange(new[]{"4","5","6"})
.Configure(list => list.Sort())
.WithFilter(x =>int.Parse(x)>)
.Build();
最佳实践和准则
在实现流畅构建器模式时,请考虑以下最佳实践:
- 使最终对象不可变。
- 使用以 “With” 或类似前缀开头的描述性方法名。
- 从配置方法中返回构建器实例。
- 提供一个清晰的
Build()
方法。 - 考虑将构建器的构造函数设为私有,并提供一个静态创建方法。
- 使用方法链来创建更自然的 API。
- 在
Build()
方法中实现验证。 - 考虑添加一个
Reset()
方法以便重用构建器。
常见陷阱及避免方法
- 过度复杂化简单对象的创建。
- 未正确处理空值。
- 在构建对象后使构建器可变。
- 未实现适当的验证。
- 创建过多的嵌套构建器。
- 未提供重置构建器的方法。
何时使用流畅构建器模式
流畅构建器模式在以下情况下最为有用:
- 对象的构建需要许多参数。
- 对象的构建涉及多个步骤。
- 需要强制实现不可变性。
- 希望提供更具可读性的 API。
- 需要构建带有可选参数的对象。
- 处理复杂的嵌套对象。
流畅构建器模式是你 C# 工具包中的强大工具。如果实现得当,它可以显著提高代码的可读性和可维护性。通过研究 .NET 标准库中的实际示例并理解高级场景,你可以在代码中明智地决定何时以及如何实现该模式。
请记住,虽然该模式很强大,但对于简单对象的创建并非总是必要的。当提高可读性和可维护性的好处超过实现该模式所带来的额外复杂性时,再使用它。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-02,如有侵权请联系 cloudcommunity@tencent 删除基础c#builderfluent对象发布者:admin,转转请注明出处:http://www.yc00.com/web/1747990555a4715857.html
评论列表(0条)