何かの列挙/コレクションであるクラスを実装する必要があることがよくあります。このスレッドのために考えてみましょう不自然な例のIniFileContent
ラインの列挙/コレクションです。
このクラスがコードベースに存在しなければならない理由は、ビジネスロジックがいたるところに広がらないようにする(=をカプセル化するwhere
)ためであり、可能な限りオブジェクト指向の方法でそれを実行したいからです。
通常、以下のように実装します。
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();
}
IEnumerable<string>
使用が便利になるので、実装を選択します。
foreach(var line in new IniFileContent(...))
{
//...
}
しかし、そうすることでクラスの意図を「隠す」のだろうか?人がIniFileContent
インターフェースを見たときだけ見るEnumerator<string> GetEnumerator()
。クラスが実際に提供しているサービスは自明ではないと思います。
次に、この2番目の実装を検討します。
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(";"));
}
}
これはあまり便利ではありません(ちなみに、new X().Y()
クラスの設計に問題があるように見えます)。
foreach(var line in new IniFileContent2(...).Lines())
{
//...
}
しかし、IEnumerable<string> Lines()
このクラスが実際に何ができるかを明らかにする明確なインターフェースがあります。
どの実装を促進しますか、そしてその理由は?暗黙のうちに、何かの列挙を表すためにIEnumerableを実装することは良い習慣ですか?
私は次の方法についての答えを探していません。
- このコードの単体テスト
- クラスの代わりに静的関数を作る
- このコードを将来のビジネスロジックの進化の影響を受けやすくする
- パフォーマンスを最適化する
付録
ここに私のコードベースに実装されている実際のコードの種類がありますIEnumerable
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();
}
IniFileContent
ください。