Introducción
Aunque es posible implementar Fluent Api con clases realmente la limitación de algunos lenguajes con la herencia múltiple hace que la costumbre sea usar interfaces, de hecho resulta más útil ya que es posible crear diferentes implementaciones especialmente en sistemas de configuración.

Lo más relevante de cara a las interfaces es:
- El nombre indica que contiene, esto quiere decir que no importa mucho que el nombre sea un poco largo si indica que métodos va a incluir o que se puede hacer con el, hay que tener en cuenta que el nombre de la interfaz generalmente no será visible en código.
Tampoco es inusual que el nombre interfaz se componga como una concatenación de todos las interfaces padres lo que a la hora de buscar o entender el código resulta más cómodo a falta de un diagrama a mano.
- Crear interfaces hija al añadir objetos, si el objeto interno tiene una lista y requiere añadir objetos a esa lista lo recomendable es crear una nueva interfaz para construir objetos de esa lista interna, y el método AddXX correspondiente devolverá dicha interfaz hija para que pueda ser usada para configurar ese elemento hijo, de esta forma se proporciona métodos específicos para este objeto que no pueden ser usados por otros en las interfaces superiores.
En el siguiente ejemplo se agrega un usuario y se devuelve una ICredentialBuilder que es específica para crear las credenciales de un usuario.
public ICredentialBuilder AddUser()
{
login = new Login();
logins.Add(login);
group.Logins.Add(login);
return this;
}
- Las interfaces hija siempre heredan directa o indirectamente de la principal, de lo contrario nos quedaríamos bloqueados y no podríamos acceder a los métodos de la interfaz principal para continuar creando el objeto y finalmente guardarlo a través del método sin contexto.
En el siguiente ejemplo IUserBuilder hereda de IGroupBuilder para poder añadir usuarios a un grupo, es decir que directamente no hereda de la interfaz principal IUsersBuilder pero si a través de IGroupBuilder.
public interface IUsersBuilder
{
...
}
public interface IUserBuilder : IGroupBuilder
{
ICredentialBuilder AddUser();
}
public interface IGroupBuilder : IUsersBuilder
{
IUserBuilder AddGroup(
string name = "");
}
- Solo la interfaz principal debe tener un método void, como indica la propia definición de Fluent Api en algún momento debemos poder indicar que el objeto está terminado y se puede construir, esté método debería estar solo en la interfaz principal para evitar crear objetos incompletos, ejemplos clásicos son Build(), Save(),…
Está bien, es verdad que se puede usar un método de extensión para sortear esta limitación, por ejemplo guardar resultados parciales, pero a no ser que sea necesario no es lo recomendable.
public interface IDatabaseBuilder : IConnectionBuilder, IUsersBuilder
{
string Save();
}
- Usar el mismo fichero para crearlas, si no son muchas es más cómodo tenerlas todas juntas en un fichero que en varios separados, sobre todo porque generalmente son interfaces con pocos métodos y al haber una relación jerárquica entre ellas vía herencia es más cómodo verlas todas juntas, como nombre de fichero lo más cómodo es poner el nombre de la interfaz principal que suele ser la que mejor define el uso.
namespace FluentApi
{
public interface IUsersBuilder
{
ICredentialBuilder AddLogin();
IGroupBuilder AddGroups();
}
public interface ICredentialBuilder : IUsersBuilder
{
IRoleBuilder WithCredential(
string username = "",
string password = ""
);
}
}
- Así como usar métodos AddXXX indica que la interfaz que devuelve servirá para crear objetos de una lista interna, es posible tener mayores niveles de anidación, por ejemplo listas de listas, esto se puede solucionar creando una interfaz que represente esta relación de creación de listas, dentro de la implementación se hará la correcta creación de las mismas.
En el ejemplo el método AddGroups() representa este efecto, este método devuelve otra interfaz IGroupBuilder encargada de crear la lista anidada y a su vez está interfaz tiene el método que añade elementos individuales AddGroup().
public interface IUsersBuilder
{
ICredentialBuilder AddLogin();
IGroupBuilder AddGroups();
}
public interface IGroupBuilder : IUsersBuilder
{
IUserBuilder AddGroup(
string name = "");
}
public interface IUserBuilder : IGroupBuilder
{
ICredentialBuilder AddUser();
}
- Puede ser que tengamos dos interfaces cada una de las cuales se encargue de una parte concreta de la creación del objeto, en este ejemplo hay una interfaz para crear conexiones IConnectionBuilder y otra para crear usuarios IUsersBuilder. Si necesitásemos usar ambas necesitamos una interfaz que nos las conecte, ese es el propósito de la interfaz IDatabaseBuilder.
Mantenemos una referencia a la interfaz principal y la usamos para configurar cada una de las partes concretas del objeto.
IDatabaseBuilder databaseBuilder = DatabaseBuilder.Create();
databaseBuilder
.AddLogin()
.WithCredential(
username: "login",
password: "password")
.AndRole(
database: "acme")
.AddRight(
permission: "read")
.AddRight(
permission: "write");
databaseBuilder
.WithServer(
name: "server",
port: 8000,
version: new Version("0.0.0.1"));
var json = databaseBuilder.Save();
