using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace JDWCodeVault.Patterns.Creational
{
/// <summary>
/// 플루언트 빌더 패턴의 기본 인터페이스
/// 메소드 체이닝을 통한 객체 생성을 지원합니다.
/// </summary>
/// <typeparam name="T">생성할 객체의 타입</typeparam>
public interface IObjectBuilder<T>
{
/// <summary>
/// 속성 값을 설정합니다.
/// </summary>
/// <typeparam name="TProperty">속성의 타입</typeparam>
/// <param name="property">설정할 속성</param>
/// <param name="value">설정할 값</param>
/// <returns>빌더 인스턴스 (메소드 체이닝용)</returns>
IObjectBuilder<T> Configure<TProperty>(Expression<Func<T, TProperty>> property, TProperty value);
/// <summary>
/// 조건부로 속성 값을 설정합니다.
/// </summary>
/// <typeparam name="TProperty">속성의 타입</typeparam>
/// <param name="condition">설정 조건</param>
/// <param name="property">설정할 속성</param>
/// <param name="value">설정할 값</param>
/// <returns>빌더 인스턴스 (메소드 체이닝용)</returns>
IObjectBuilder<T> ConfigureIf<TProperty>(bool condition, Expression<Func<T, TProperty>> property, TProperty value);
/// <summary>
/// 설정된 값들로 최종 객체를 생성합니다.
/// </summary>
/// <returns>생성된 객체</returns>
T Build();
/// <summary>
/// 빌더를 초기 상태로 리셋합니다.
/// </summary>
/// <returns>리셋된 빌더 인스턴스</returns>
IObjectBuilder<T> Reset();
}
/// <summary>
/// 제네릭 플루언트 빌더 구현체
/// 리플렉션을 사용하여 동적으로 객체를 생성합니다.
/// </summary>
/// <typeparam name="T">생성할 객체의 타입</typeparam>
public class FluentBuilder<T> : IObjectBuilder<T> where T : class, new()
{
private readonly Dictionary<string, object> properties;
private readonly Dictionary<string, Func<object, bool>> validators;
public FluentBuilder()
{
properties = new Dictionary<string, object>();
validators = new Dictionary<string, Func<object, bool>>();
}
public IObjectBuilder<T> Configure<TProperty>(Expression<Func<T, TProperty>> property, TProperty value)
{
var propertyName = GetPropertyName(property);
properties[propertyName] = value;
return this;
}
public IObjectBuilder<T> ConfigureIf<TProperty>(bool condition, Expression<Func<T, TProperty>> property, TProperty value)
{
if (condition)
{
Configure(property, value);
}
return this;
}
/// <summary>
/// 속성에 대한 검증 규칙을 추가합니다.
/// </summary>
/// <typeparam name="TProperty">속성의 타입</typeparam>
/// <param name="property">검증할 속성</param>
/// <param name="validator">검증 함수</param>
/// <returns>빌더 인스턴스</returns>
public FluentBuilder<T> AddValidator<TProperty>(Expression<Func<T, TProperty>> property, Func<TProperty, bool> validator)
{
var propertyName = GetPropertyName(property);
validators[propertyName] = obj => validator((TProperty)obj);
return this;
}
public T Build()
{
ValidateProperties();
var instance = new T();
var type = typeof(T);
foreach (var kvp in properties)
{
var propertyInfo = type.GetProperty(kvp.Key);
if (propertyInfo != null && propertyInfo.CanWrite)
{
propertyInfo.SetValue(instance, kvp.Value);
}
}
return instance;
}
public IObjectBuilder<T> Reset()
{
properties.Clear();
return this;
}
private void ValidateProperties()
{
foreach (var kvp in properties)
{
if (validators.TryGetValue(kvp.Key, out var validator))
{
if (!validator(kvp.Value))
{
throw new ArgumentException($"Validation failed for property: {kvp.Key}");
}
}
}
}
private static string GetPropertyName<TProperty>(Expression<Func<T, TProperty>> property)
{
if (property.Body is MemberExpression memberExpression)
{
return memberExpression.Member.Name;
}
throw new ArgumentException("Expression must be a property access", nameof(property));
}
}
/// <summary>
/// 단계별 빌더 패턴을 위한 타입 안전한 빌더
/// 컴파일 타임에 필수 속성 설정을 강제합니다.
/// </summary>
/// <typeparam name="T">생성할 객체의 타입</typeparam>
public class StepBuilder<T> where T : class
{
protected readonly Dictionary<string, object> properties;
protected StepBuilder()
{
properties = new Dictionary<string, object>();
}
protected StepBuilder(Dictionary<string, object> existingProperties)
{
properties = new Dictionary<string, object>(existingProperties);
}
protected TNext CreateNext<TNext>() where TNext : StepBuilder<T>, new()
{
var next = new TNext();
foreach (var kvp in properties)
{
next.properties[kvp.Key] = kvp.Value;
}
return next;
}
}
/// <summary>
/// 불변 객체 생성을 위한 빌더
/// 빌드 시점에 모든 속성이 설정된 불변 객체를 생성합니다.
/// </summary>
/// <typeparam name="T">생성할 불변 객체의 타입</typeparam>
public class ImmutableBuilder<T> : IObjectBuilder<T> where T : class
{
private readonly Dictionary<string, object> properties;
private readonly Func<Dictionary<string, object>, T> factory;
public ImmutableBuilder(Func<Dictionary<string, object>, T> factory)
{
this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
properties = new Dictionary<string, object>();
}
public IObjectBuilder<T> Configure<TProperty>(Expression<Func<T, TProperty>> property, TProperty value)
{
var propertyName = GetPropertyName(property);
properties[propertyName] = value;
return this;
}
public IObjectBuilder<T> ConfigureIf<TProperty>(bool condition, Expression<Func<T, TProperty>> property, TProperty value)
{
if (condition)
{
Configure(property, value);
}
return this;
}
public T Build()
{
return factory(new Dictionary<string, object>(properties));
}
public IObjectBuilder<T> Reset()
{
properties.Clear();
return this;
}
private static string GetPropertyName<TProperty>(Expression<Func<T, TProperty>> property)
{
if (property.Body is MemberExpression memberExpression)
{
return memberExpression.Member.Name;
}
throw new ArgumentException("Expression must be a property access", nameof(property));
}
}
/// <summary>
/// 빌더 팩토리 - 다양한 빌더 타입을 생성합니다.
/// </summary>
public static class BuilderFactory
{
/// <summary>
/// 플루언트 빌더를 생성합니다.
/// </summary>
/// <typeparam name="T">생성할 객체의 타입</typeparam>
/// <returns>플루언트 빌더 인스턴스</returns>
public static FluentBuilder<T> CreateFluent<T>() where T : class, new()
{
return new FluentBuilder<T>();
}
/// <summary>
/// 불변 객체 빌더를 생성합니다.
/// </summary>
/// <typeparam name="T">생성할 불변 객체의 타입</typeparam>
/// <param name="factory">객체 생성 팩토리 함수</param>
/// <returns>불변 객체 빌더 인스턴스</returns>
public static ImmutableBuilder<T> CreateImmutable<T>(Func<Dictionary<string, object>, T> factory) where T : class
{
return new ImmutableBuilder<T>(factory);
}
}
}
평가 부탁드립니다.
adhd가 보기엔 현학적임 ㅈㅅ
ㄳ
ㅋ