Saya sering menemukan diri saya perlu mengimplementasikan kelas yang merupakan enumerasi / koleksi sesuatu. Pertimbangkan untuk thread ini dengan contoh buat dari IniFileContent
yang merupakan pencacahan / kumpulan garis.
Alasan kelas ini harus ada dalam basis kode saya adalah bahwa saya ingin menghindari logika bisnis yang tersebar di semua tempat (= merangkum where
) dan saya ingin melakukannya dengan cara yang paling berorientasi objek mungkin.
Biasanya saya akan mengimplementasikannya seperti di bawah ini:
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();
}
Saya memilih untuk menerapkan IEnumerable<string>
karena membuat penggunaannya nyaman:
foreach(var line in new IniFileContent(...))
{
//...
}
Namun saya bertanya-tanya apakah hal itu "membayangi" niat kelas? Ketika seseorang melihat IniFileContent
antarmuka, ia hanya akan melihat Enumerator<string> GetEnumerator()
. Saya pikir itu membuat tidak jelas layanan mana yang sebenarnya disediakan kelas.
Pertimbangkan implementasi kedua ini:
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(";"));
}
}
Yang digunakan kurang nyaman (omong-omong, melihat new X().Y()
terasa seperti ada yang salah dengan desain kelas):
foreach(var line in new IniFileContent2(...).Lines())
{
//...
}
Tetapi dengan antarmuka yang IEnumerable<string> Lines()
jelas membuat jelas apa yang sebenarnya dapat dilakukan oleh kelas ini.
Implementasi mana yang akan Anda bina dan mengapa? Tersirat, apakah itu praktik yang baik untuk mengimplementasikan IEnumerable untuk mewakili enumerasi sesuatu?
Saya tidak mencari jawaban tentang cara:
- unit uji kode ini
- membuat fungsi statis, bukan kelas
- membuat kode ini lebih rentan terhadap evolusi logika bisnis di masa depan
- mengoptimalkan kinerja
Lampiran
Berikut adalah jenis kode nyata yang tinggal di basis kode saya yang mengimplementasikanIEnumerable
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();
}
new
untuk kasus penggunaan ini, kesulitan dalam pengujian unit, ambigu ketika saya / O pengecualian dapat terjadi, dll. Maaf, saya tidak berusaha bersikap kasar. Saya pikir saya sudah terlalu terbiasa dengan manfaat injeksi ketergantungan .
IniFileContent
.