Skip to content

Commit

Permalink
feat: add builder pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
treveshan committed Jun 6, 2024
1 parent dd56ede commit 94a534f
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 1 deletion.
95 changes: 95 additions & 0 deletions src/Creational/Builder/Builder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using DesignPatterns.Creational.FactoryMethod.PolicyCreators;
using DesignPatterns.Utils.Display;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.Diagnostics.Contracts;
using System.Diagnostics.Metrics;
using System.IO;
using System.Linq.Expressions;
using System.Net.NetworkInformation;
using System.Numerics;
using System.Reflection.Emit;
using System.Runtime.Intrinsics.X86;
using System.Security.Cryptography;
using System.Threading.Channels;
using System;
using DesignPatterns.Creational.Builder;

namespace DesignPatterns.Creational.FactoryMethod;

public class Builder
{
private readonly IOutput _output;

public Builder(IOutput output)
{
_output = output;
}

public void Run(string policyType)
{

//#### Advantages:

//1. * *Improves Readability and Maintainability * *:
// -The Builder pattern enhances the readability of the code by separating the construction of complex objects from their representation. It makes the code easier to understand and maintain.

//2. * *Supports Step - by - Step Construction * *:
// -This pattern allows for constructing complex objects step by step.You can create different configurations of an object by varying the director's steps.

//3. * *Encapsulates Construction Code * *:
// -The construction logic is encapsulated in the builder, which helps in organizing the code better and adhering to the Single Responsibility Principle.

//4. * *Fluent Interface * *:
// -Using fluent syntax, the Builder pattern provides an expressive way to construct objects, making the code more readable and easier to use.

//5. * *Immutable Objects * *:
// -Builders can be used to create immutable objects by only setting the values once during the construction phase and not allowing changes afterward.

//6. * *Reusability * *:
// -The same builder can be reused to create different representations of the product by changing the sequence of construction steps.

//#### Disadvantages:

//1. * *More Complex Code * *:
// -The Builder pattern introduces additional classes and interfaces, which can increase the complexity of the codebase, especially for simpler objects where the pattern might be overkill.

//2. * *Verbosity * *:
// -The pattern can lead to verbose code, as you need to create a builder class, director class, and the product class. This verbosity might not be justified for simple objects.

//3. **Requires More Effort**:
// - Implementing the Builder pattern requires more effort compared to straightforward object construction. It might not be beneficial if the complexity of object construction is low.

//4. **Director Dependency**:
// - The director can add an extra layer of dependency, which might not always be necessary. It can sometimes complicate the design if not used judiciously.

//5. **Learning Curve**:
// - For developers unfamiliar with design patterns, understanding and implementing the Builder pattern might require a learning curve, adding to the initial development time.

//### When to Use the Builder Pattern

//- **Complex Construction Logic**: When the construction of an object involves many steps, and the object has many configurations.
//- **Immutability**: When you need to create immutable objects and want to provide a clear and concise way to construct them.
//- **Readable Code**: When you want to improve the readability and maintainability of the code by separating the construction logic.
//- **Reusable Builders**: When you need to reuse the construction code to create different representations of the product.

//The Builder pattern is powerful for creating complex objects with many configurations, but it should be used judiciously to avoid unnecessary complexity in the code.

var builder = new InsurancePolicyBuilder();
var director = new InsurancePolicyDirector();

switch (policyType)
{
case "Comprehensive":
InsurancePolicy comprehensivePolicy = director.ConstructComprehensivePolicy(builder);
_output.Display(comprehensivePolicy.ToString());
break;
case "Basic":
InsurancePolicy basicPolicy = director.ConstructBasicPolicy(builder);
_output.Display(basicPolicy.ToString());
break;
default:
_output.Display("Invalid option.");
break;
}
}
}
9 changes: 9 additions & 0 deletions src/Creational/Builder/IInsurancePolicyBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace DesignPatterns.Creational.Builder;

public interface IInsurancePolicyBuilder
{
IInsurancePolicyBuilder WithPolicyType(string policyType);
IInsurancePolicyBuilder WithPremium(double premium);
IInsurancePolicyBuilder WithCoverAmount(double coverAmount);
InsurancePolicy Build();
}
13 changes: 13 additions & 0 deletions src/Creational/Builder/InsurancePolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace DesignPatterns.Creational.Builder;

public class InsurancePolicy
{
public string PolicyType { get; set; }

Check warning on line 5 in src/Creational/Builder/InsurancePolicy.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'PolicyType' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 5 in src/Creational/Builder/InsurancePolicy.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'PolicyType' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
public double Premium { get; set; }
public double CoverAmount { get; set; }

public override string ToString()
{
return $"Policy Type: {PolicyType}, Premium: {Premium:C0}, Cover Amount: {CoverAmount:C0}";
}
}
29 changes: 29 additions & 0 deletions src/Creational/Builder/InsurancePolicyBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace DesignPatterns.Creational.Builder;

public class InsurancePolicyBuilder : IInsurancePolicyBuilder
{
private readonly InsurancePolicy _policy = new();

public IInsurancePolicyBuilder WithPolicyType(string policyType)
{
_policy.PolicyType = policyType;
return this;
}

public IInsurancePolicyBuilder WithPremium(double premium)
{
_policy.Premium = premium;
return this;
}

public IInsurancePolicyBuilder WithCoverAmount(double coverAmount)
{
_policy.CoverAmount = coverAmount;
return this;
}

public InsurancePolicy Build()
{
return _policy;
}
}
22 changes: 22 additions & 0 deletions src/Creational/Builder/InsurancePolicyDirector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace DesignPatterns.Creational.Builder;

public class InsurancePolicyDirector
{
public InsurancePolicy ConstructComprehensivePolicy(IInsurancePolicyBuilder builder)
{
return builder
.WithPolicyType("Comprehensive")
.WithPremium(1500.00)
.WithCoverAmount(500000.00)
.Build();
}

public InsurancePolicy ConstructBasicPolicy(IInsurancePolicyBuilder builder)
{
return builder
.WithPolicyType("Basic")
.WithPremium(500.00)
.WithCoverAmount(100000.00)
.Build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DesignPatterns.Creational.Builder;
using FluentAssertions;

namespace DesignPatterns.Creational.Tests.Builder
{
public class InsurancePolicyBuilderTests
{
[Test]
public void ShouldBuildComprehensivePolicyCorrectly()
{
// Arrange
var builder = new InsurancePolicyBuilder();
var director = new InsurancePolicyDirector();

// Act
InsurancePolicy policy = director.ConstructComprehensivePolicy(builder);

// Assert
policy.PolicyType.Should().Be("Comprehensive");
policy.Premium.Should().Be(1500.00);
policy.CoverAmount.Should().Be(500000.00);
}

[Test]
public void ShouldBuildBasicPolicyCorrectly()
{
// Arrange
var builder = new InsurancePolicyBuilder();
var director = new InsurancePolicyDirector();

// Act
InsurancePolicy policy = director.ConstructBasicPolicy(builder);

// Assert
policy.PolicyType.Should().Be("Basic");
policy.Premium.Should().Be(500.00);
policy.CoverAmount.Should().Be(100000.00);
}

[Test]
public void ShouldBuildCustomPolicyCorrectlyUsingFluentSyntax()
{
// Arrange
var builder = new InsurancePolicyBuilder();

// Act
InsurancePolicy policy = builder
.WithPolicyType("Custom")
.WithPremium(1200.00)
.WithCoverAmount(300000.00)
.Build();

// Assert
policy.PolicyType.Should().Be("Custom");
policy.Premium.Should().Be(1200.00);
policy.CoverAmount.Should().Be(300000.00);
}
}
}
29 changes: 28 additions & 1 deletion src/DesignPatterns/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
.Title("Please choose pattern to run:")
.PageSize(10)
.AddChoices(new[] {
"Factory Method","Abstract Factory", "Exit"
"Factory Method","Abstract Factory","Builder", "Exit"
}));

switch (choice)
Expand All @@ -45,6 +45,9 @@
case "Abstract Factory":
RunAbstractFactory();
break;
case "Builder":
RunBuilderFactory();
break;
case "Exit":
exit = true;
break;
Expand Down Expand Up @@ -113,4 +116,28 @@ void RunAbstractFactory()
}
}
}
void RunBuilderFactory()
{
var abstractFactory = new Builder(new ConsoleOutput());
var exit = false;
while (!exit)
{
var option = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title("Please choose type of insurance policy:")
.PageSize(10)
.AddChoices(new[] {
"Comprehensive","Basic", "Exit"
}));
if (!string.IsNullOrEmpty(option))
{
if (option.ToLower() == "exit")
{
exit = true;
break;
}
abstractFactory.Run(option);
}
}
}

0 comments on commit 94a534f

Please sign in to comment.