Type-State Builder Pattern
The type-state builder pattern uses interface progression to enforce compile-time safety, ensuring objects are constructed with all required properties in the correct order. Invalid states become impossible to represent.
Interface Progression
using System.Net;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Mvc;
namespace Shared.Types;
public interface IProblemBuilder<T>
{
IProblemBuilderWithSource<T> WithSourceId(
string sourceId);
}
public interface IProblemBuilderWithSource<T>
{
IProblemBuilderWithTitle<T> WithTitle(string title);
}
public interface IProblemBuilderWithTitle<T>
{
IProblemBuilderWithDetail<T> WithDetail(
string detail);
}
public interface IProblemBuilderWithDetail<T>
{
IProblemBuilderComplete<T> WithStatusCode(
HttpStatusCode statusCode);
}
public interface IProblemBuilderComplete<T>
{
ProblemDetailsResult<T> Create(
[CallerMemberName] string callerMemberName = "");
}
Implementation
internal class ProblemBuilder<T> :
IProblemBuilder<T>,
IProblemBuilderWithSource<T>,
IProblemBuilderWithTitle<T>,
IProblemBuilderWithDetail<T>,
IProblemBuilderComplete<T>
{
private string? _sourceId;
private string? _title;
private string? _detail;
private HttpStatusCode _statusCode;
public IProblemBuilderWithSource<T> WithSourceId(
string sourceId)
{
_sourceId = sourceId;
return this;
}
public IProblemBuilderWithTitle<T> WithTitle(
string title)
{
_title = title;
return this;
}
public IProblemBuilderWithDetail<T> WithDetail(
string detail)
{
_detail = detail;
return this;
}
public IProblemBuilderComplete<T> WithStatusCode(
HttpStatusCode statusCode)
{
_statusCode = statusCode;
return this;
}
public ProblemDetailsResult<T> Create(
[CallerMemberName] string callerMemberName = "")
{
return new ProblemDetailsResult<T>
{
SourceId = _sourceId!,
StatusCode = _statusCode,
ProblemDetails = new ProblemDetails
{
Title = _title!,
Detail = _detail!,
Status = (int)_statusCode
},
CallerMemberName = callerMemberName
};
}
}
SOLID Principles Demonstrated
Single Responsibility Principle: Each interface represents one step in the construction process
Open/Closed Principle: Extensible without modifying existing interfaces
Interface Segregation Principle: Clients depend only on interfaces they need at each step
Dependency Inversion Principle: Factory methods depend on abstractions, not concrete builder
See Also
10 November 2025