# Fluent Builder --- ### A simple example ```csharp var fruitBasket = ABasketOfFruits() .WithApples( new GrannySmith(), new PinkLady(), new GoldenDelicious()) .WithBananas(new Banana(), new Banana(), new Banana()) .WithGrapes(new Grapes()) .Create(); ``` --- ### Basic Builder - Creational Pattern (see GoF book) - helps to create complex objects - helps to separate complex creational logic - avoids to expose internals of complex objects to the user --- ### Fluent Builder - build steps are chainable with `.` operator - helps to create a Domain Specific Language (DSL) --- ### Idea - accumulate parts of the complex object within an intermediate object (the actual builder) - use separate `Build()` step to actually create the final object - builder can be pushed around to collect the several parts --- ### Our Fruits ```csharp public record Fruit {} public record Apple : Fruit {} public record GrannySmith : Apple {} public record GoldenDelicious : Apple {} public record PinkLady : Apple {} public record Banana : Fruit {} public record Grapes : Fruit {} ``` --- ### Full example (classic builder) ```csharp new class FruitBasketBuilder { private List<Apple> Apples { get; } = new List<Apple>(); private List<Banana> Bananas { get; } = new List<Banana>(); private Grapes? Grapes { get; set; } public static FruitBasketBuilder ABasketOfFruits() => new FruitBasketBuilder(); public void WithApples(params Apple[] apples) => Apples.AddRange(apples); public void WithBananas(params Banana[] bananas) => Bananas.AddRange(bananas); public void WithGrapes(Grapes grapes) => Grapes = grapes; public FruitBasket Create() { var fruits = Apples.Cast<Fruit>().Concat(Bananas).ToList(); if(Grapes is not null) fruits.Add(Grapes); return new FruitBasket(fruits); } } ``` --- ### Static Factory Method Advantages over constructor: - can be named freely (and expressively) - multiple methods possible, also with same signature, but different behavior ```csharp new class FruitBasketBuilder { // ... public static FruitBasketBuilder ABasketOfFruits() => new FruitBasketBuilder(); // ... } ``` --- ### Accumulating Parts ```csharp private List<Apple> Apples { get; } = new List<Apple>(); private List<Banana> Bananas { get; } = new List<Banana>(); private Grapes? Grapes { get; set; } // ... public void WithApples(params Apple[] apples) => Apples.AddRange(apples); public void WithBananas(params Banana[] bananas) => Bananas.AddRange(bananas); public void WithGrapes(Grapes grapes) => Grapes = grapes; // ... ``` --- ### Building the basket - assemble all collected parts into the final object - constraints of the final object can be handled within the build method ```csharp new class FruitBasketBuilder { public FruitBasket Create() { var fruits = Apples.Cast<Fruit>().Concat(Bananas).ToList(); if(Grapes is not null) fruits.Add(Grapes); return new FruitBasket(fruits); } private List<Apple> Apples { get; } = new List<Apple>(); private List<Banana> Bananas { get; } = new List<Banana>(); private Grapes? Grapes { get; set; } } ``` --- ### Fluently chain To allow fluently chaining the steps, just return the builder itself: ```csharp // ... public FruitBasketBuilder WithApples(params Apple[] apples) { Apples.AddRange(apples); return this; } // ... ``` ---
{"metaMigratedAt":"2023-06-17T03:25:25.152Z","metaMigratedFrom":"YAML","title":"Technical Review Team Revit","breaks":true,"description":"an introduction into the fluent builder","contributors":"[{\"id\":\"d1e9ea52-bb3e-4e50-a92e-731218618a3b\",\"add\":8595,\"del\":7305}]"}
    411 views