Skip to content

Instantly share code, notes, and snippets.

@jhgbrt
Last active August 27, 2015 08:32
Show Gist options
  • Save jhgbrt/74e67c4781b935aa311b to your computer and use it in GitHub Desktop.
Save jhgbrt/74e67c4781b935aa311b to your computer and use it in GitHub Desktop.
Basic Functional Fluent Builder
using System;
using System.Collections.Generic;
// Factory methods provided in non-generic abstract base class
public abstract class Builder
{
public static Builder<T> For<T>() where T : new()
{
return new Builder<T>(() => new T());
}
public static Builder<T> For<T>(Func<T> ctor)
{
return new Builder<T>(ctor);
}
}
// generic builder
public class Builder<T> : Builder
{
private readonly Func<T> _ctor;
private readonly List<Action<T>> _configureActions = new List<Action<T>>();
internal Builder(Func<T> ctor)
{
_ctor = ctor;
}
// construct the instance to be built: calls the constructor and all configured actions
public T Instance
{
get
{
var instance = _ctor();
foreach (var configure in _configureActions)
{
configure(instance);
}
return instance;
}
}
public Builder<T> With(Action<T> action)
{
_configureActions.Add(action);
return this;
}
}
#region examples
public static class ProductBuilder
{
// use extension methods to provide more expressive convenience builder functions
public static Builder<Product> Named(this Builder<Product> builder, string name)
{
return builder.With(p => p.Name = name);
}
// use static factory methods to construct objects with preconfigured defaults
public static Builder<Product> DefaultProduct()
{
return Builder.For<Product>().Named("My Product").With(p => p.UnitPrice = 10);
}
}
class Order
{
private readonly List<OrderLine> _lines;
public Order()
{
Id = Guid.NewGuid().ToString();
_lines = new List<OrderLine>();
}
public Order(string id)
{
Id = id;
_lines = new List<OrderLine>();
}
public string Id { get; private set; }
public IEnumerable<OrderLine> Lines
{
get { return _lines; }
}
public Customer Customer { get; set; }
public void AddLine(Product p, int nofItems)
{
var orderLine = new OrderLine(p, nofItems);
_lines.Add(orderLine);
OrderTotal += orderLine.LineTotal;
}
public decimal OrderTotal { get; set; }
}
class Customer
{
public string Name { get; set; }
public string Address { get; set; }
}
class OrderLine
{
public OrderLine(Product product, int nofItems)
{
Product = product;
NumberOfItems = nofItems;
LineTotal = product.UnitPrice * nofItems;
}
public decimal LineTotal { get; private set; }
public Product Product { get; private set; }
public int NumberOfItems { get; private set; }
}
public class Product
{
public string Name { get; set; }
public decimal UnitPrice { get; set; }
}
public class Examples
{
public void TestMethod()
{
var product = ProductBuilder.DefaultProduct().Named("Another product").Instance;
int nofItems = 3;
var order = Builder.For<Order>().Instance;
order.AddLine(product, nofItems);
Assert.AreEqual(15, order.OrderTotal);
}
}
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment