Progressive Interfaces
March 21, 2009David wrote this post about a month ago where he challenged the usefulness of fluent interfaces. One of his concerns is the discoverability of a fluent API in order to determine what the correct syntax should be. Whenever you're new to a particular fluent API, it may not always be obvious what the API developer had in mind when he designed it. This can be a pain point and certainly when you don't have any examples of how the fluent API syntax should look like.
This can be improved, however, by using progressive interfaces. This is certainly nothing new and is already being used by popular fluent API's like the one provided by StructureMap.
Let's start by showing a 'traditional' expression builder like the one I provided in my previous post:
public class ProductBuilder
{
private String Manufacturer { get; set; }
private String Name { get; set; }
private Double Price { get; set; }
public static ProductBuilder CreateProduct()
{
return new ProductBuilder();
}
public ProductBuilder Named(String name)
{
Name = name;
return this;
}
public ProductBuilder ManufacturedBy(
String manufacturer)
{
Manufacturer = manufacturer;
return this;
}
public ProductBuilder Priced(Double price)
{
Price = price;
return this;
}
public static implicit operator Product(
ProductBuilder builder)
{
return builder.Build();
}
private Product Build()
{
return new Product(Name, Manufacturer, Price);
}
}
Notice that I've implemented method chaining by letting the ProductBuilder return an instance of itself. Whenever I'm using Intellisense for discovering the API of an object, I get to see all the methods provided by the expression builder.
For this simple example it isn't really a problem. But imagine for a moment that we as the designers of this fluent API want our users to first provide the name, then the manufacturer and last but not least the price of a Product. In order to automatically fall into the pit of success, we can use progressive interfaces to clearly communicate our intent. First we need to create/extract the necessary interfaces that we will be using as a return type for every consecutive method call of our expression builder.
public interface IPreProductNameBuilder
{
IPostProductNameBuilder Named(String name);
}
public interface IPostProductNameBuilder
{
IPostProductManufacturerBuilder ManufacturedBy(
String manufacturer);
}
public interface IPostProductManufacturerBuilder
{
Product Priced(Double price);
}
Next step is to adjust the methods of the expression builder so that they no longer returns its the type of the expression builder itself but one of these progressive interfaces instead. Notice that the ProductBuilder class also implements every interface we just defined. This way the ProductBuilder can keep returning the instance of itself. Below is the modified code of the ProductBuilder that now uses the progressive interfaces we just defined.
public class ProductBuilder : IPreProductNameBuilder,
IPostProductNameBuilder,
IPostProductManufacturerBuilder
{
private String Manufacturer { get; set; }
private String Name { get; set; }
private Double Price { get; set; }
public static IPreProductNameBuilder CreateProduct()
{
return new ProductBuilder();
}
public IPostProductNameBuilder Named(String name)
{
Name = name;
return this;
}
public IPostProductManufacturerBuilder ManufacturedBy(
String manufacturer)
{
Manufacturer = manufacturer;
return this;
}
public Product Priced(Double price)
{
Price = price;
return this;
}
public static implicit operator Product(
ProductBuilder builder)
{
return builder.Build();
}
private Product Build()
{
return new Product(Name, Manufacturer, Price);
}
}
Now the intended syntax for the fluent API can easily be discovered by just using Intellisense.
Using progressive interfaces may be slightly more work (but as always, Reshaper is your friend). The gain however is making your fluent interfaces much easier to discover by expressing your intent to the poor guy that needs to consume them..
If you and your team want to learn more about how to write maintainable unit tests and get the most out of TDD practices, make sure to have look at our trainings and workshops or check out the books section. Feel free to reach out at info. @ principal-it .be
Jan Van Ryswyck
Thank you for visiting my blog. I’m a professional software developer since Y2K. A blogger since Y2K+5. Provider of training and coaching in XP practices. Curator of the Awesome Talks list. Past organizer of the European Virtual ALT.NET meetings. Thinking and learning about all kinds of technologies since forever.
Comments
Writing Maintainable
Unit Tests
Watch The Videos
Latest articles
-
Contract Tests - Parameterised Test Cases
June 28, 2023
-
Contract Tests - Abstract Test Cases
April 12, 2023
-
Contract Tests
February 1, 2023
-
The Testing Quadrant
June 15, 2022
-
Tales Of TDD: The Big Refactoring
February 2, 2022
Tags
- .NET
- ALT.NET
- ASP.NET
- Agile
- Announcement
- Architecture
- Behavior-Driven Development
- C++
- CQRS
- Clojure
- CoffeeScript
- Community
- Concurrent Programming
- Conferences
- Continuous Integration
- Core Skills
- CouchDB
- Database
- Design Patterns
- Domain-Driven Design
- Event Sourcing
- F#
- Fluent Interfaces
- Functional Programming
- Hacking
- Humor
- Java
- JavaScript
- Linux
- Microsoft
- NHibernate
- NoSQL
- Node.js
- Object-Relational Mapping
- Open Source
- Reading
- Ruby
- Software Design
- SourceControl
- Test-Driven Development
- Testing
- Tools
- Visual Studio
- Web
- Windows
Disclaimer
The opinions expressed on this blog are my own personal opinions. These do NOT represent anyone else’s view on the world in any way whatsoever.
About
Thank you for visiting my website. I’m a professional software developer since Y2K. A blogger since Y2K+5. Author of Writing Maintainable Unit Tests. Provider of training and coaching in XP practices. Curator of the Awesome Talks list. Thinking and learning about all kinds of technologies since forever.
Latest articles
Contract Tests - Parameterised Test Cases
Contract Tests - Abstract Test Cases
Contract Tests
The Testing Quadrant
Contact information
(+32) 496 38 00 82
info @ principal-it .be