Created
September 29, 2016 19:48
-
-
Save boca/7ad4ae836a5515afbc87e0e585024222 to your computer and use it in GitHub Desktop.
The Curiosly Recursive Generic Pattern
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) {Id = id;Name = name;} | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
} | |
public class ProductBuilder | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
public ProductBuilder ManufacturedBy(Manufacturer aManufacturer) => Fluent(() => manufacturer = aManufacturer); | |
public ProductBuilder WithSku(string aSku) => Fluent(() => sku = aSku); | |
public ProductBuilder Named(string aName) => Fluent(() => name = aName); | |
public ProductBuilder PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
public void Save() | |
{ | |
if (manufacturer == null) throw new InvalidOperationException("Manufacturer should be set because foreign keys"); | |
DoSave(Build()); | |
} | |
protected ProductBuilder Fluent(Action a) => this.Fluently(a); | |
protected void DoSave(Product product) { product.Dump(); } | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var dell = new Manufacturer("m001", "DELL"); | |
new ProductBuilder() | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.WithSku("001") | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.ManufacturedBy(dell) | |
.Save(); | |
new ProductBuilder() | |
.ManufacturedBy(dell) | |
.WithSku("001") | |
.Named("Xps laptop") | |
.PricedAt(499.99) | |
.Save(); | |
//we want the compiler to tell us that the next line is invalid | |
new ProductBuilder().Save(); | |
} | |
public class ProductBuilder | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
public ProductBuilder ManufacturedBy(Manufacturer aManufacturer) => Fluent(() => manufacturer = aManufacturer); | |
public ProductBuilder WithSku(string aSku) => Fluent(() => sku = aSku); | |
public ProductBuilder Named(string aName) => Fluent(() => name = aName); | |
public ProductBuilder PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
public void Save() | |
{ | |
if (manufacturer == null) throw new InvalidOperationException("Manufacturer should be set because foreign keys"); | |
DoSave(Build()); | |
} | |
protected ProductBuilder Fluent(Action a) => this.Fluently(a); | |
protected void DoSave(Product product) { product.Dump(); } | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var dell = new Manufacturer("m001", "DELL"); | |
new ProductBuilder() | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.WithSku("001") | |
.Build() | |
.Dump(); | |
//This doesn't work anymore since only ProductSavers can save | |
new ProductBuilder() | |
.ManufacturedBy(dell) | |
.Save(); | |
//This doesn't work anymore since only ProductSavers can save | |
new ProductBuilder() | |
.ManufacturedBy(dell) | |
.WithSku("001") | |
.Named("Xps laptop") | |
.PricedAt(499.99) | |
.Save(); | |
//This doesn't work anymore since only ProductSavers can save | |
new ProductBuilder().Save(); | |
} | |
public class ProductBuilder | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
public ProductBuilder ManufacturedBy(Manufacturer aManufacturer) => Fluent(() => manufacturer = aManufacturer); | |
public ProductBuilder WithSku(string aSku) => Fluent(() => sku = aSku); | |
public ProductBuilder Named(string aName) => Fluent(() => name = aName); | |
public ProductBuilder PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
protected ProductBuilder Fluent(Action a) => this.Fluent(a); | |
protected void DoSave(Product product) { product.Dump(); } | |
} | |
public class ProductSaver : ProductBuilder | |
{ | |
public ProductSaver(Manufacturer aManufacturer) { manufacturer = aManufacturer; } | |
public void Save() => DoSave(Build()); | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var dell = new Manufacturer("m001", "DELL"); | |
new ProductBuilder() | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.WithSku("001") | |
.Build() | |
.Dump(); | |
new ProductSaver(dell) | |
.Save(); | |
//This is invalid now, which is what we wanted | |
new ProductSaver().Save(); | |
//but we broke this one too | |
new ProductSaver(dell) | |
.WithSku("001") | |
.Named("Xps laptop") | |
.PricedAt(499.99) | |
.Save(); | |
} | |
public class ProductBuilder | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
public ProductBuilder ManufacturedBy(Manufacturer aManufacturer) => Fluent(() => manufacturer = aManufacturer); | |
public ProductBuilder WithSku(string aSku) => Fluent(() => sku = aSku); | |
public ProductBuilder Named(string aName) => Fluent(() => name = aName); | |
public ProductBuilder PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
protected ProductBuilder Fluent(Action a) => this.Fluent(a); | |
protected void DoSave(Product product) { product.Dump(); } | |
} | |
public class ProductSaver : ProductBuilder | |
{ | |
public ProductSaver(Manufacturer aManufacturer) { manufacturer = aManufacturer; } | |
public void Save() => DoSave(Build()); | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var dell = new Manufacturer("m001", "DELL"); | |
//product builders are broken since it's now a generic class | |
new ProductBuilder() | |
.Build() | |
.Dump(); | |
//product builders are broken since it's now a generic class | |
new ProductBuilder() | |
.WithSku("001") | |
.Build() | |
.Dump(); | |
new ProductSaver(dell) | |
.Save(); | |
new ProductSaver(dell) | |
.WithSku("001") | |
.Named("Xps laptop") | |
.PricedAt(499.99) | |
.Save(); | |
//still not valid. good | |
new ProductSaver().Save(); | |
} | |
public class ProductBuilder<TSelf> | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
public TSelf ManufacturedBy(Manufacturer aManufacturer) => Fluent(() => manufacturer = aManufacturer); | |
public TSelf WithSku(string aSku) => Fluent(() => sku = aSku); | |
public TSelf Named(string aName) => Fluent(() => name = aName); | |
public TSelf PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
protected TSelf Fluent(Action a) => this.Fluently(a); | |
protected void DoSave(Product product) { product.Dump(); } | |
} | |
public class ProductSaver : ProductBuilder<ProductSaver> | |
{ | |
public ProductSaver(Manufacturer aManufacturer) { manufacturer = aManufacturer; } | |
public void Save() => DoSave(Build()); | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var dell = new Manufacturer("m001", "DELL"); | |
new ProductBuilder() | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.WithSku("001") | |
.Build() | |
.Dump(); | |
new ProductSaver(dell) | |
.Save(); | |
new ProductSaver(dell) | |
.WithSku("001") | |
.Named("Xps laptop") | |
.PricedAt(499.99) | |
.Save(); | |
//still not valid. good | |
//new ProductSaver().Save(); | |
} | |
public abstract class ProductBuilder<TSelf> | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
public TSelf ManufacturedBy(Manufacturer aManufacturer) => Fluent(() => manufacturer = aManufacturer); | |
public TSelf WithSku(string aSku) => Fluent(() => sku = aSku); | |
public TSelf Named(string aName) => Fluent(() => name = aName); | |
public TSelf PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
protected abstract TSelf This { get; } | |
protected TSelf Fluent(Action a) => This.Fluently(a); | |
protected void DoSave(Product product) { product.Dump(); } | |
} | |
public class ProductBuilder : ProductBuilder<ProductBuilder> { protected override ProductBuilder This => this; } | |
public class ProductSaver : ProductBuilder<ProductSaver> | |
{ | |
public ProductSaver(Manufacturer aManufacturer) { manufacturer = aManufacturer; } | |
public void Save() => DoSave(Build()); | |
protected override ProductSaver This => this; | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var dell = new Manufacturer("m001", "DELL"); | |
new ProductBuilder() | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.WithSku("001") | |
.Build() | |
.Dump(); | |
new ProductSaver(dell) | |
.Save(); | |
new ProductSaver(dell) | |
.WithSku("001") | |
.Named("Xps laptop") | |
.PricedAt(499.99) | |
.Save(); | |
new StupidProductBuilder() | |
.Build(); | |
//broken because This returns a string so it breaks our chaining | |
new StupidProductBuilder() | |
.WithSku("0") | |
.Build(); | |
//still invaild. good | |
//new ProductSaver().Save(); | |
} | |
public abstract class ProductBuilder<TSelf> | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
public TSelf ManufacturedBy(Manufacturer aManufacturer) => Fluent(() => manufacturer = aManufacturer); | |
public TSelf WithSku(string aSku) => Fluent(() => sku = aSku); | |
public TSelf Named(string aName) => Fluent(() => name = aName); | |
public TSelf PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
protected abstract TSelf This { get; } | |
protected TSelf Fluent(Action a) => This.Fluently(a); | |
protected void DoSave(Product product) { product.Dump(); } | |
} | |
//This is nonsense. It's valid, but string is not what we meant by TSelf | |
public class StupidProductBuilder : ProductBuilder<string> { protected override string This => "stupid"; } | |
public class ProductBuilder : ProductBuilder<ProductBuilder> { protected override ProductBuilder This => this; } | |
public class ProductSaver : ProductBuilder<ProductSaver> | |
{ | |
public ProductSaver(Manufacturer aManufacturer) { manufacturer = aManufacturer; } | |
public void Save() => DoSave(Build()); | |
protected override ProductSaver This => this; | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var dell = new Manufacturer("m001", "DELL"); | |
new ProductBuilder() | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.WithSku("001") | |
.Build() | |
.Dump(); | |
new ProductSaver(dell) | |
.Save(); | |
new ProductSaver(dell) | |
.WithSku("001") | |
.Named("Xps laptop") | |
.PricedAt(499.99) | |
.Save(); | |
//still invalid. good | |
//new ProductSaver().Save(); | |
} | |
//Curiously Recursive Template Pattern CRTP | |
//Curiously Recursive Generic Pattern CRGP | |
// this where clause is an f-bound | |
public abstract class ProductBuilder<TSelf> where TSelf : ProductBuilder<TSelf> | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
public TSelf ManufacturedBy(Manufacturer aManufacturer) => Fluent(() => manufacturer = aManufacturer); | |
public TSelf WithSku(string aSku) => Fluent(() => sku = aSku); | |
public TSelf Named(string aName) => Fluent(() => name = aName); | |
public TSelf PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
protected abstract TSelf This { get; } | |
protected TSelf Fluent(Action a) => This.Fluently(a); | |
protected void DoSave(Product product) { product.Dump(); } | |
} | |
//No longer valid. The f-bound prevents this nonsense | |
//public class StupidProductBuilder: ProductBuilder<string> { protected override string This => "stupid";} | |
//This is what we meant to happen | |
public class StupidProductBuilder: ProductBuilder<StupidProductBuilder> { protected override StupidProductBuilder This => this;} | |
//but I still can create stupid things like this. F-bound will only carry me until here. | |
public class StupidProductBuilder: ProductBuilder<ProductSaver> { protected override ProductSaver This => null;} | |
public class ProductBuilder : ProductBuilder<ProductBuilder> { protected override ProductBuilder This => this; } | |
public class ProductSaver : ProductBuilder<ProductSaver> | |
{ | |
public ProductSaver(Manufacturer aManufacturer) { manufacturer = aManufacturer; } | |
public void Save() => DoSave(Build()); | |
protected override ProductSaver This => this; | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var dell = new Manufacturer("m001", "DELL"); | |
new ProductBuilder() | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.WithSku("001") | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.ManufacturedBy(dell) | |
.Save(); | |
new ProductBuilder() | |
.ManufacturedBy(dell) | |
.WithSku("001") | |
.Named("Xps laptop") | |
.PricedAt(499.99) | |
.Save(); | |
new ProductBuilder().Save(); | |
} | |
public abstract class HasManufacturer { } | |
public class WithManufacturer : HasManufacturer { } | |
public class WithoutManufacturer : HasManufacturer { } | |
public class ProductBuilder<T> where T : HasManufacturer | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
protected ProductBuilder() { } | |
private ProductBuilder(Manufacturer aManufacturer, string aSku, string aName, double aPrice) | |
{ | |
manufacturer = aManufacturer; | |
sku = aSku; | |
name = aName; | |
aPrice = price; | |
} | |
public ProductBuilder<WithManufacturer> ManufacturedBy(Manufacturer aManufacturer) => new ProductBuilder<WithManufacturer>(aManufacturer, sku, name, price); | |
public ProductBuilder<T> WithSku(string aSku) => Fluent(() => sku = aSku); | |
public ProductBuilder<T> Named(string aName) => Fluent(() => name = aName); | |
public ProductBuilder<T> PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
public void Save() | |
{ | |
if (manufacturer == null) throw new InvalidOperationException("Manufacturer should be set because foreign keys"); | |
DoSave(Build()); | |
} | |
protected ProductBuilder<T> Fluent(Action a) => this.Fluently(a); | |
protected void DoSave(Product product) { product.Dump(); } | |
} | |
public class ProductBuilder : ProductBuilder<WithoutManufacturer> { } | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var dell = new Manufacturer("m001", "DELL"); | |
new ProductBuilder() | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.WithSku("001") | |
.Build() | |
.Dump(); | |
new ProductBuilder() | |
.ManufacturedBy(dell) | |
.Save(); | |
new ProductBuilder() | |
.ManufacturedBy(dell) | |
.WithSku("001") | |
.Named("Xps laptop") | |
.PricedAt(499.99) | |
.Save(); | |
//now is invalid | |
new ProductBuilder().Save(); | |
} | |
public abstract class HasManufacturer { } | |
public class WithManufacturer : HasManufacturer { } | |
public class WithoutManufacturer : HasManufacturer { } | |
public class ProductBuilder<T> where T : HasManufacturer | |
{ | |
protected Manufacturer manufacturer; | |
protected string sku; | |
protected string name; | |
protected double price; | |
protected ProductBuilder() { } | |
private ProductBuilder(Manufacturer aManufacturer, string aSku, string aName, double aPrice) | |
{ | |
manufacturer = aManufacturer; | |
sku = aSku; | |
name = aName; | |
aPrice = price; | |
} | |
public ProductBuilder<WithManufacturer> ManufacturedBy(Manufacturer aManufacturer) => new ProductBuilder<WithManufacturer>(aManufacturer, sku, name, price); | |
public ProductBuilder<T> WithSku(string aSku) => Fluent(() => sku = aSku); | |
public ProductBuilder<T> Named(string aName) => Fluent(() => name = aName); | |
public ProductBuilder<T> PricedAt(double aPrice) => Fluent(() => price = aPrice); | |
public Product Build() => new Product { Manufacturer = manufacturer, Sku = sku, Name = name, Price = price }; | |
protected ProductBuilder<T> Fluent(Action a) => this.Fluently(a); | |
} | |
public class ProductBuilder : ProductBuilder<WithoutManufacturer> { } | |
public static class ProductBuilderExtensions | |
{ | |
public static void Save(this ProductBuilder<WithManufacturer> builder) => DoSave(builder.Build()); | |
private static void DoSave(Product product) { product.Dump(); } | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t; } | |
} | |
public class Manufacturer | |
{ | |
public Manufacturer(string id, string name) { Id = id; Name = name; } | |
public string Id { get; set; } | |
public string Name { get; set; } | |
} | |
public class Product | |
{ | |
public string Sku { get; set; } | |
public Manufacturer Manufacturer { get; set; } | |
public string Name { get; set; } | |
public double Price { get; set; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var r1= Request | |
.ForAnalyticsByCountry | |
.WithCountry("us"); | |
var r2= Request | |
.ForOmnitureDataByCountryLanguage | |
.WithCountry("us") | |
.WithLanguage("en"); | |
var r3 =Request | |
.ForClickDataByLwp | |
.WithCountry("us") | |
.WithLanguage("en") | |
.WithSegment("gen") | |
.WithCustomerSet("19"); | |
DummyDataRetriever.RetrieveDataWithFilters(r1); | |
DummyDataRetriever.RetrieveDataWithFilters(r2); | |
DummyDataRetriever.RetrieveDataWithFilters(r3); | |
} | |
public abstract class Request | |
{ | |
protected Dictionary<string, object> Filters = new Dictionary<string, object>(); | |
public static AnalyticsByCountry ForAnalyticsByCountry => new AnalyticsByCountry(); | |
public static OmnitureDataByCountryLanguage ForOmnitureDataByCountryLanguage => new OmnitureDataByCountryLanguage(); | |
public static ClickDataByLwp ForClickDataByLwp => new ClickDataByLwp(); | |
public static implicit operator Dictionary<string,object>(Request r) => r.Filters; | |
} | |
public abstract class Request<T>:Request where T:Request<T> | |
{ | |
protected T Fluent(Action a) => This.Fluently(a); | |
protected abstract T This { get; } | |
protected T Country (string country ) => Fluent(() => Filters["Country" ] = country ); | |
protected T Language (string language ) => Fluent(() => Filters["Language" ] = language ); | |
protected T Segment (string segment ) => Fluent(() => Filters["Segment" ] = segment ); | |
protected T CustomerSet(string customerSet) => Fluent(() => Filters["CustomerSet"] = customerSet); | |
} | |
public class AnalyticsByCountry : Request<AnalyticsByCountry> | |
{ | |
protected override AnalyticsByCountry This => this; | |
public AnalyticsByCountry WithCountry(string country) => Country(country); | |
} | |
public class OmnitureDataByCountryLanguage : Request<OmnitureDataByCountryLanguage> | |
{ | |
protected override OmnitureDataByCountryLanguage This => this; | |
public OmnitureDataByCountryLanguage WithCountry (string country ) => Country (country) ; | |
public OmnitureDataByCountryLanguage WithLanguage(string language) => Language(language); | |
} | |
public class ClickDataByLwp : Request<ClickDataByLwp> | |
{ | |
protected override ClickDataByLwp This => this; | |
public ClickDataByLwp WithCountry (string country ) => Country (country) ; | |
public ClickDataByLwp WithLanguage (string language) => Language (language); | |
public ClickDataByLwp WithSegment (string segment) => Segment (segment); | |
public ClickDataByLwp WithCustomerSet(string customerSet) => CustomerSet(customerSet); | |
} | |
public class DummyDataRetriever | |
{ | |
public static object RetrieveDataWithFilters(Dictionary<string, object> filters) => filters.Dump(); | |
} | |
public static class FluentExtensions | |
{ | |
public static T Fluently<T>(this T t, Action a) { a(); return t;} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment