1.図書館は自給自足である必要がありますか?
ライブラリは、たとえば、依存性注入フレームワークを確実に使用できます。ただし、ライブラリのユーザーにDIフレームワークを強制するかどうかは議論の余地があります。
2.リスコフの置換原則はポリモーフィズムにどのように適合しますか?
Liskovの置換原則は、ある実装を別の実装と交換できるようにすること(置換)であり、動作に関係なく、ユーザーが契約を順守するときにエラーが発生することはありません。たとえば、をaに交換するCanonPrinter
と、クラスEpsonPrinter
のメソッドのみを使用する場合でも印刷できるはずです。Printer
何かを呼び出すPrinter
が、その基礎となる特定の実装(Canon、Epson)を持つことは、ポリモーフィズムです。
3.インターフェースを実装とは別のフォルダーに保持するのは悪い習慣ですか?
インターフェースを実装から切り離したいかどうかは個人的な好みです。私はそれをしません。実装ごとのインターフェースすらありません。プロジェクトに付加価値を与える場合にのみ、実装用のインターフェースを用意することは理にかなっていると思います。
4.実装だけでなく、インターフェイスのジェネリック型を制限することも悪い習慣ですか?
ジェネリック型を特定の型に限定する必要があると思われる場合は、それが理にかなっている場所で行う必要があります(したがって、これはインターフェイス上にある可能性があります)。たとえば、オブジェクトにIPrintableDocument<TPrinter>
限定TPrinter
せずにを設定しても意味がないので、そうします。Printer
5.オブジェクトの関係が「できる」と「ある」の両方である場合、インターフェースは抽象クラスよりも有利ですか?
実際、ほとんどの人は抽象クラスを使用し、インターフェースは関係を実行できます。理由:クラスは1つの基本クラスからのみ継承できますが、複数のインターフェースから継承できます。本質的に、これは、クラスが1つのもの(aEmployee
は Person
)である可能性がありますICopyDocuments
が、複数のこと(、、、である可能性がIWalkAbout
あります)を実行できることを意味しIMakeCoffee
ます。インターフェースが両方であるときに何をするかは、好みによって異なります。
質問のほとんどは、クラス(またはインターフェイス)のコントラクトに関係しています。コントラクトは、ユーザー(別のクラス、他の誰かのコード)がクラスとそのメンバーに対して実行できることと実行できないことを指定し、ユーザーが何を実行するかを指定します。クラスが実行することと実行しないことを期待する場合があります。
例:ユーザーは、オブジェクトがパラメータータイプと一致する場合にのみ、オブジェクトを引数としてメソッドに渡すことができます。クラスは、戻り型に一致する戻り値としてオブジェクトのみを返します。ユーザーはクラスで定義されたメンバーのみを呼び出すことができ、クラスはそれらのメソッドがコントラクトに従って動作することを確認します(つまり、エラーをスローせず、副作用がなく、nullを返さない)。
ジェネリック型の制約も契約の一部です。コントラクトのドキュメント部分も考慮します。メソッドが例外をスローしないと記載されている場合、実装は例外をスローしてはなりません。それを強制する構文やコンパイラ規則はありませんが、それは契約の一部です。コントラクトの一部はコンパイラーまたはランタイムによって強制されますが、そうでない部分もあります。
クラスまたはインターフェースに特定のコントラクトがある場合、任意のサブクラスまたは実装をその代わりに使用でき、そのサブクラスまたは実装がコントラクトに準拠している場合でも機能します。それが契約に準拠している場合、それはリスコフの置換原則(LSP)です。そして、それから逸脱するのは簡単であり、多くのプログラマーはそうします。時々あなたは選択の余地がありません。
.NET FrameworkでのLSP違反の明確な例は、ReadOnlyCollection<T>
クラスです。インターフェイスを実装しIList<T>
ますが、多くのメソッドの実際の実装はありません。したがって、IList<T>
aReadOnlyCollection<T>
を期待しているユーザーを渡し、そのユーザーがを呼び出そうとするとlist.Add
、例外がスローされます。したがって、常に代わりに使用できるとは限らないIList<T>
ためReadOnlyCollection<T>
、LSPに違反します。