I have implemented currency conversion result creation using the builder pattern in C#. Below is my original code without the builder pattern:
var verboseResult = new VerboseCurrencyConversionResult
{
Date = date,
Currency = fromCurrency,
Denominator = toCurrency,
Description = $"Currency conversion from {fromCurrency} to {toCurrency}",
};
if (hasFromCurrency && hasToCurrency)
{
verboseResult.Calculated = true;
verboseResult.Source = Source;
verboseResult.Value = Math.Round((double)(partsFromCurrency.Value ?? 0) / (double)(partsToCurrency.Value ?? 1), 4);
verboseResult.Parts = new List<Part> { partsFromCurrency, partsToCurrency };
}
else
{
verboseResult.Calculated = false;
verboseResult.Source = partsFromCurrency?.Description;
verboseResult.Value = partsFromCurrency?.Value;
verboseResult.Parts = null;
}
if (!verbose)
{
var nonVerboseResult = new BasicCurrencyConversionResult
{
Date = verboseResult.Date,
Currency = verboseResult.Currency,
Denominator = verboseResult.Denominator,
Value = verboseResult.Value
};
return nonVerboseResult;
}
return verboseResult;
I’ve refactored it using the builder pattern. Here is the updated code with the builder pattern implementation:
var resultBuilder = new CurrencyConversionResultBuilder()
.WithDate(date)
.WithCurrency(fromCurrency)
.WithDenominator(toCurrency);
var verboseBuilder = resultBuilder.ToVerbose();
if (hasFromCurrency && hasToCurrency)
{
verboseBuilder
.WithCalculated(true)
.WithSource(Source)
.WithParts(new List<Part> { partsFromCurrency, partsToCurrency })
.WithValue(Math.Round((double)(partsFromCurrency.Value ?? 0) / (double)(partsToCurrency.Value ?? 1), 4));
}
else
{
verboseBuilder
.WithCalculated(false)
.WithSource(Source)
.WithParts(null)
.WithValue(partsFromCurrency.Value);
}
if (!verbose)
{
return resultBuilder.WithValue(verboseBuilder.Build().Value).Build();
}
return verboseBuilder.WithExchangeDescription($"Currency conversion from {fromCurrency} to {toCurrency}").Build();
Here are my builder classes:
public class CurrencyConversionResultBuilder
{
private BasicCurrencyConversionResult _result;
public CurrencyConversionResultBuilder()
{
_result = new BasicCurrencyConversionResult();
}
public CurrencyConversionResultBuilder WithDate(string date)
{
_result.Date = date;
return this;
}
public CurrencyConversionResultBuilder WithCurrency(string currency)
{
_result.Currency = currency;
return this;
}
public CurrencyConversionResultBuilder WithDenominator(string denominator)
{
_result.Denominator = denominator;
return this;
}
public virtual CurrencyConversionResultBuilder WithValue(double? value)
{
_result.Value = value;
return this;
}
public BasicCurrencyConversionResult Build() => _result;
public VerboseCurrencyConversionResultBuilder ToVerbose()
{
return new VerboseCurrencyConversionResultBuilder(_result);
}
}
public class VerboseCurrencyConversionResultBuilder : CurrencyConversionResultBuilder
{
private VerboseCurrencyConversionResult _verboseResult;
public VerboseCurrencyConversionResultBuilder(BasicCurrencyConversionResult result)
{
_verboseResult = new VerboseCurrencyConversionResult
{
Date = result.Date,
Currency = result.Currency,
Denominator = result.Denominator,
Value = result.Value
};
}
public VerboseCurrencyConversionResultBuilder WithExchangeDescription(string description)
{
_verboseResult.Description = description;
return this;
}
public VerboseCurrencyConversionResultBuilder WithCalculated(bool calculated)
{
_verboseResult.Calculated = calculated;
return this;
}
public VerboseCurrencyConversionResultBuilder WithSource(string source)
{
_verboseResult.Source = source;
return this;
}
public VerboseCurrencyConversionResultBuilder WithParts(List<Part> parts)
{
_verboseResult.Parts = parts;
return this;
}
public override CurrencyConversionResultBuilder WithValue(double? value)
{
_verboseResult.Value = value;
return this;
}
public new VerboseCurrencyConversionResult Build() => _verboseResult;
}
Does my implementation of the builder pattern look correct and follow best practices? Are there any improvements or modifications you would suggest to enhance this pattern or the overall code structure?