A menudo encuentro que necesito implementar una clase que es una enumeración / colección de algo. Considere para este hilo el ejemplo artificial del IniFileContentcual es una enumeración / colección de líneas.
La razón por la que esta clase debe existir en mi base de código es porque quiero evitar que la lógica de negocios se extienda por todo el lugar (= encapsulando el where) y quiero hacerlo de la manera más orientada a objetos posible.
Por lo general, lo implementaría de la siguiente manera:
public sealed class IniFileContent : IEnumerable<string>
{
private readonly string _filepath;
public IniFileContent(string filepath) => _filepath = filepath;
public IEnumerator<string> GetEnumerator()
{
return File.ReadLines(_filepath)
.Where(l => !l.StartsWith(";"))
.GetEnumerator();
}
public IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Elijo implementar IEnumerable<string>porque hace que su uso sea conveniente:
foreach(var line in new IniFileContent(...))
{
//...
}
Sin embargo, me pregunto si, al hacerlo, "sombrea" la intención de la clase Cuando uno mira IniFileContentla interfaz, solo se ve Enumerator<string> GetEnumerator(). Creo que no es obvio qué servicio proporciona realmente la clase.
Considere entonces esta segunda implementación:
public sealed class IniFileContent2
{
private readonly string _filepath;
public IniFileContent2(string filepath) => _filepath = filepath;
public IEnumerable<string> Lines()
{
return File.ReadLines(_filepath)
.Where(l => !l.StartsWith(";"));
}
}
Que se usa menos convenientemente (por cierto, ver new X().Y()que algo salió mal con el diseño de la clase):
foreach(var line in new IniFileContent2(...).Lines())
{
//...
}
Pero con una interfaz clara que IEnumerable<string> Lines()hace obvio lo que esta clase realmente puede hacer.
¿Qué implementación fomentarías y por qué? Implicado, ¿es una buena práctica implementar IEnumerable para representar una enumeración de algo?
No estoy buscando respuestas sobre cómo:
- prueba de unidad de este código
- hacer una función estática en lugar de una clase
- hacer que este código sea más propenso a la futura evolución de la lógica empresarial
- optimizar el rendimiento
Apéndice
Este es el tipo de código real que vive en mi base de código que implementaIEnumerable
public class DueInvoices : IEnumerable<DueInvoice>
{
private readonly IEnumerable<InvoiceDto> _invoices;
private readonly IEnumerable<ReminderLevel> _reminderLevels;
public DueInvoices(IEnumerable<InvoiceDto> invoices, IEnumerable<ReminderLevel> reminderLevels)
{
_invoices = invoices;
_reminderLevels = reminderLevels;
}
public IEnumerator<DueInvoice> GetEnumerator() => _invoices.Where(invoice => invoice.DueDate < DateTime.Today && !invoice.Paid)
.Select(invoice => new DueInvoice(invoice, _reminderLevels))
.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
newpara este caso de uso, dificultad en las pruebas unitarias, ambigua cuando yo / O pueden ocurrir excepciones, etc. Lo siento, no estoy tratando de ser grosero. Creo que me he acostumbrado demasiado a los beneficios de la inyección de dependencia .
IniFileContent.