tipos - Interfaces fluidas y herencia en C#
que es una interfaz en programacion (6)
¿Es realmente una mejor interfaz una interfaz fluida, o un inicializador sería mejor?
var p = new Person{
LastName = "Smith",
FirstName = "John"
};
var c = new Customer{
LastName = "Smith",
FirstName = "John",
AccountNumber = "000",
ID = "123"
};
A diferencia de una interfaz fluida, esto funciona bien sin métodos heredados que devuelvan la clase base y arruinen la cadena. Cuando hereda una propiedad, la persona que llama realmente no debería preocuparse de si FirstName
se implementó por primera vez en Person, Customer o Object.
También me parece más legible, ya sea en una línea o en múltiples, y no tiene que pasar por el problema de proporcionar funciones de auto-decoración fluidas que se correspondan con cada propiedad.
Mostraré un problema con el ejemplo. Hay una clase base con una interfaz fluida:
class FluentPerson
{
private string _FirstName = String.Empty;
private string _LastName = String.Empty;
public FluentPerson WithFirstName(string firstName)
{
_FirstName = firstName;
return this;
}
public FluentPerson WithLastName(string lastName)
{
_LastName = lastName;
return this;
}
public override string ToString()
{
return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
}
}
y una clase de niños:
class FluentCustomer : FluentPerson
{
private long _Id;
private string _AccountNumber = String.Empty;
public FluentCustomer WithAccountNumber(string accountNumber)
{
_AccountNumber = accountNumber;
return this;
}
public FluentCustomer WithId(long id)
{
_Id = id;
return this;
}
public override string ToString()
{
return base.ToString() + String.Format(" account number: {0} id: {1}", _AccountNumber, _Id);
}
}
El problema es que cuando llama al customer.WithAccountNumber("000").WithFirstName("John").WithLastName("Smith")
no puede agregar .WithId(123)
al final porque el tipo de devolución de WithLastName()
método es FluentPerson (no FluentCustomer).
¿Cómo este problema generalmente se resuelve?
Intenta usar algunos métodos de extensión.
static class FluentManager
{
public static T WithFirstName<T>(this T person, string firstName) where T : FluentPerson
{
person.FirstName = firstName;
return person;
}
public static T WithId<T>(this T customer, long id) where T : FluentCustomer
{
customer.ID = id;
return customer;
}
}
class FluentPerson
{
public string FirstName { private get; set; }
public string LastName { private get; set; }
public override string ToString()
{
return string.Format("First name: {0} last name: {1}", FirstName, LastName);
}
}
class FluentCustomer : FluentPerson
{
public long ID { private get; set; }
public long AccountNumber { private get; set; }
public override string ToString()
{
return base.ToString() + string.Format(" account number: {0} id: {1}", AccountNumber, ID);
}
}
después de que puedas usar como
new FluentCustomer().WithId(22).WithFirstName("dfd").WithId(32);
Lógicamente, debe configurar las cosas desde la más específica (cliente) hasta la menos específica (persona) o de lo contrario es incluso difícil de leer a pesar de la fluida interfaz. Siguiendo esta regla en la mayoría de los casos no necesitarás meterse en problemas. Sin embargo, si por alguna razón aún necesita mezclarlo, puede usar declaraciones de énfasis intermedio como
static class Customers
{
public static Customer AsCustomer(this Person person)
{
return (Customer)person;
}
}
customer.WIthLastName("Bob").AsCustomer().WithId(10);
Puedes usar genéricos para lograr eso.
public class FluentPerson<T>
where T : FluentPerson<T>
{
public T WithFirstName(string firstName)
{
// ...
return (T)this;
}
public T WithLastName(string lastName)
{
// ...
return (T)this;
}
}
public class FluentCustomer : FluentPerson<FluentCustomer>
{
public FluentCustomer WithAccountNumber(string accountNumber)
{
// ...
return this;
}
}
Y ahora:
var customer = new FluentCustomer()
.WithAccountNumber("123")
.WithFirstName("Abc")
.WithLastName("Def")
.ToString();
Una solución donde necesita una interfaz fluida, herencia y también algunos genéricos ...
De todos modos, como dije antes: esta es la única opción si desea utilizar la herencia y acceder también a los miembros protegidos ...
public class GridEx<TC, T> where TC : GridEx<TC, T> { public TC Build(T type) { return (TC) this; } } public class GridExEx : GridEx<GridExEx, int> { } class Program { static void Main(string[] args) { new GridExEx().Build(1); } }
public class FluentPerson
{
private string _FirstName = String.Empty;
private string _LastName = String.Empty;
public FluentPerson WithFirstName(string firstName)
{
_FirstName = firstName;
return this;
}
public FluentPerson WithLastName(string lastName)
{
_LastName = lastName;
return this;
}
public override string ToString()
{
return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
}
}
public class FluentCustomer
{
private string _AccountNumber = String.Empty;
private string _id = String.Empty;
FluentPerson objPers=new FluentPerson();
public FluentCustomer WithAccountNumber(string accountNumber)
{
_AccountNumber = accountNumber;
return this;
}
public FluentCustomer WithId(string id)
{
_id = id;
return this;
}
public FluentCustomer WithFirstName(string firstName)
{
objPers.WithFirstName(firstName);
return this;
}
public FluentCustomer WithLastName(string lastName)
{
objPers.WithLastName(lastName);
return this;
}
public override string ToString()
{
return objPers.ToString() + String.Format(" account number: {0}", _AccountNumber);
}
}
E invocarlo usando
var ss = new FluentCustomer().WithAccountNumber("111").WithFirstName("ram").WithLastName("v").WithId("444").ToString();