亦称:构造者、生成器、Builder

建造者是一种创建型设计模式, 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。

💡 适合应用场景

当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,可以考虑使用建造者模式。
特别是当你需要创建一个可能有许多配置选项的对象时, 该模式会特别有用。比如.NETCoreIHost就是通过一个个的配置对象构建而成。如下:

  1. public class Program
  2. {
  3. public static void Main(string[] args)
  4. {
  5. IHostBuilder hostBuilder = Host
  6. .CreateDefaultBuilder(args)
  7. .ConfigureWebHostDefaults(webBuilder =>
  8. {
  9. webBuilder.ConfigureServices(services =>
  10. {
  11. services.AddControllers();
  12. });
  13. webBuilder.Configure(app =>
  14. {
  15. app.UseRouting();
  16. app.UseAuthorization();
  17. app.UseEndpoints(endpoints =>
  18. {
  19. endpoints.MapControllers();
  20. });
  21. });
  22. });
  23. IHost host = hostBuilder.Build();
  24. host.Run();
  25. }
  26. }

🖇 结构

image.png

# 代码示例

实现简易Sql构建

  1. public interface ISql
  2. {
  3. string ConnectionString { get; }
  4. bool IsUseMonitor { get; }
  5. void Insert();
  6. }
  7. public enum DbType
  8. {
  9. MySql,
  10. SqlServer
  11. }
  12. public class SqlBuilder
  13. {
  14. private string _connectionString;
  15. private DbType _dbType;
  16. private bool _isUseMonitor;
  17. public SqlBuilder() { }
  18. public SqlBuilder UseConnectionString(DbType dbType, string connectionString)
  19. {
  20. _dbType = dbType;
  21. _connectionString = connectionString;
  22. return this;
  23. }
  24. public SqlBuilder UseMonitor(bool isUseMonitor)
  25. {
  26. _isUseMonitor = isUseMonitor;
  27. return this;
  28. }
  29. public ISql Build()
  30. {
  31. Type type = null;
  32. switch (_dbType)
  33. {
  34. case DbType.MySql:
  35. type = typeof(MySql);
  36. break;
  37. default:
  38. throw new NotImplementedException("未实现!");
  39. }
  40. ISql sql = Activator.CreateInstance(type, new object[] { _connectionString, _isUseMonitor }) as ISql;
  41. return sql;
  42. }
  43. }
  44. public class MySql : ISql
  45. {
  46. public string ConnectionString { get; protected set; }
  47. public bool IsUseMonitor { get; protected set; }
  48. public MySql(string connectionString, bool isUseMonitor)
  49. {
  50. ConnectionString = connectionString;
  51. IsUseMonitor = isUseMonitor;
  52. }
  53. public void Insert()
  54. {
  55. if (IsUseMonitor)
  56. {
  57. Console.WriteLine("开启了监视!");
  58. }
  59. Console.WriteLine(DbType.MySql.ToString() + ",插入了一条数据!");
  60. }
  61. }
  62. public class Program
  63. {
  64. public static void Main(string[] args)
  65. {
  66. // 使用
  67. var sql = new SqlBuilder()
  68. .UseConnectionString(DbType.MySql, "192.168.0.1")
  69. .UseMonitor(true)
  70. .Build();
  71. sql.Insert();
  72. }
  73. }
  1. 开启了监视!
  2. MySql,插入了一条数据!

传统模式

  1. // Builder 接口指定了创建 Product 对象不同部分的方法。
  2. public interface IBuilder
  3. {
  4. void BuildPartA();
  5. void BuildPartB();
  6. void BuildPartC();
  7. }
  1. // Concrete Builder 类遵循 Builder 接口并提供构建步骤的特定实现。
  2. // 您的程序可能有几种不同的构建器,实现方式不同。
  3. public class ConcreteBuilder : IBuilder
  4. {
  5. private Product _product = new Product();
  6. // 一个新的构建器实例应该包含一个空白产品对象,用于进一步的组装。
  7. public ConcreteBuilder()
  8. {
  9. this.Reset();
  10. }
  11. public void Reset()
  12. {
  13. this._product = new Product();
  14. }
  15. // 所有生产步骤都使用相同的产品实例。
  16. public void BuildPartA()
  17. {
  18. this._product.Add("PartA1");
  19. }
  20. public void BuildPartB()
  21. {
  22. this._product.Add("PartB1");
  23. }
  24. public void BuildPartC()
  25. {
  26. this._product.Add("PartC1");
  27. }
  28. // Concrete Builders 应该提供他们自己的方法来检索结果。
  29. // 这是因为不同类型的构建者可能会创建完全不同的产品,它们不遵循相同的界面。
  30. // 因此,此类方法不能在基本 Builder 接口中声明(至少在静态类型的编程语言中)。
  31. //
  32. // 通常,在将最终结果返回给客户端后,构建器实例应该准备好开始生产另一个产品。
  33. // 这就是为什么通常在 `GetProduct` 方法体的末尾调用 reset 方法的原因。
  34. // 但是,此行为不是强制性的,您可以让构建器在处理先前的结果之前等待来自客户端代码的显式重置调用。
  35. public Product GetProduct()
  36. {
  37. Product result = this._product;
  38. this.Reset();
  39. return result;
  40. }
  41. }
  1. // 仅当您的产品非常复杂且需要大量配置时,才有意义使用 Builder 模式。
  2. //
  3. // 与其他创造模式不同,不同的混凝土建造者可以生产不相关的产品。
  4. // 换句话说,不同构建器的结果可能并不总是遵循相同的界面。
  5. public class Product
  6. {
  7. private List<object> _parts = new List<object>();
  8. public void Add(string part)
  9. {
  10. this._parts.Add(part);
  11. }
  12. public string ListParts()
  13. {
  14. string str = string.Empty;
  15. for (int i = 0; i < this._parts.Count; i++)
  16. {
  17. str += this._parts[i] + ", ";
  18. }
  19. // 删除最后一个",c"
  20. str = str.Remove(str.Length - 2);
  21. return "Product parts: " + str + "\n";
  22. }
  23. }
  1. // Director 仅负责按特定顺序执行构建步骤。
  2. // 在根据特定订单或配置生产产品时很有帮助。
  3. // 严格来说,Director 类是可选的,因为客户端可以直接控制构建器。
  4. public class Director
  5. {
  6. private IBuilder _builder;
  7. public IBuilder Builder
  8. {
  9. set { _builder = value; }
  10. }
  11. // Director 可以使用相同的构建步骤构建多个产品变体。
  12. public void BuildMinimalViableProduct()
  13. {
  14. this._builder.BuildPartA();
  15. }
  16. public void BuildFullFeaturedProduct()
  17. {
  18. this._builder.BuildPartA();
  19. this._builder.BuildPartB();
  20. this._builder.BuildPartC();
  21. }
  22. }
  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. // 客户端代码创建一个构建器对象,将其传递给主管,然后启动构建过程。
  6. // 最终结果是从构建器对象中检索的。
  7. var director = new Director();
  8. var builder = new ConcreteBuilder();
  9. director.Builder = builder;
  10. Console.WriteLine("Standard basic product:");
  11. director.BuildMinimalViableProduct();
  12. Console.WriteLine(builder.GetProduct().ListParts());
  13. Console.WriteLine("Standard full featured product:");
  14. director.BuildFullFeaturedProduct();
  15. Console.WriteLine(builder.GetProduct().ListParts());
  16. // 请记住,Builder 模式可以在没有 Director 类的情况下使用。
  17. Console.WriteLine("Custom product:");
  18. builder.BuildPartA();
  19. builder.BuildPartC();
  20. Console.Write(builder.GetProduct().ListParts());
  21. }
  22. }

最终执行结果:

  1. Standard basic product:
  2. Product parts: PartA1
  3. Standard full featured product:
  4. Product parts: PartA1, PartB1, PartC1
  5. Custom product:
  6. Product parts: PartA1, PartC1