Muitas vezes me vejo precisando implementar uma classe que é uma enumeração / coleção de algo. Considere para este segmento o exemplo inventado do IniFileContentqual é uma enumeração / coleção de linhas.
A razão pela qual essa classe deve existir na minha base de código é que eu quero evitar que a lógica de negócios seja espalhada por todo o lugar (= encapsulando the where) e quero fazê-lo da maneira mais orientada a objetos possível.
Normalmente eu o implementaria como abaixo:
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();
}
Eu escolho implementar IEnumerable<string>porque torna seu uso conveniente:
foreach(var line in new IniFileContent(...))
{
//...
}
No entanto, eu estou querendo saber se isso "obscurece" a intenção da classe? Quando alguém olha para IniFileContenta interface, apenas vê Enumerator<string> GetEnumerator(). Eu acho que não é óbvio qual serviço a classe está realmente fornecendo.
Considere então esta segunda implementação:
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 é usado de forma menos conveniente (a propósito, ver new X().Y()parece que algo deu errado com o design da classe):
foreach(var line in new IniFileContent2(...).Lines())
{
//...
}
Mas com uma interface clara IEnumerable<string> Lines()tornando óbvio o que essa classe pode realmente fazer.
Qual implementação você adotaria e por quê? Implícito, é uma boa prática implementar IEnumerable para representar uma enumeração de algo?
Não estou procurando respostas sobre como:
- teste de unidade este código
- faça uma função estática em vez de uma classe
- torne esse código mais propenso à evolução futura da lógica de negócios
- otimizar o desempenho
Apêndice
Aqui está o tipo de código real que vive na minha 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, dificuldade no teste de unidade, ambígua quando I / O exceções podem ocorrer, etc. Sinto muito, não estou tentando ser rude. Acho que me acostumei demais aos benefícios da injeção de dependência .
IniFileContent.