インターフェースvs. クラス

さまざまな人々-主に経験豊富な開発者-と話し合う古典的な作品「オブジェクト指向設計のテクニック。 デザインパターン»ガンマ、ヘルマなど、基本的なOOPアプローチの1つであるクラスとインターフェイスの違いの完全な誤解に驚いた。







この本の著者にとって、この質問は非常に透明であるため、読者に明らかなはずであると仮定して、2ページはほとんど割いていません。 そして、実際、これは私にとって疑問を引き起こしませんでした-少し前に、概念の率直な誤解で一度に複数のプログラマーに会ったとき、私はその本質を説明する言葉すら見つけることができなかったほど明白でした。



したがって、私はこの記事の問題に対する私の理解を体系化しようとしました。



クラスとインターフェイスの主な違いは、クラスがインターフェイスと実装で構成されていることです。



クラスは常に暗黙的にそのインターフェイスを宣言します-外部からクラスを使用するときに利用できるもの。 Keyクラスがあり、プライベートのInsert、Rotate、およびRemoveメソッドを呼び出すパブリックOpenメソッドがある場合、KeyクラスのインターフェイスはOpenメソッドで構成されます。 Keyクラスからクラスを継承すると、このインターフェイスが継承されます。



このインターフェイスに加えて、クラスには実装もあります。Insert、Rotate、Removeメソッドと、Openメソッドでの呼び出しです。 キーの継承者は、インターフェイスと実装とともに継承します。



そして、ここに問題が潜んでいます。 キーを使用してドアを開く特定のモデルがあるとします。 彼女はKeyインターフェイスを知っているため、Openメソッドを呼び出します。



しかし、いくつかのドアは回転式キーではなく磁気カードで開かれていると仮定します-これは本質的にキーでもあります! このカードのインターフェースは、通常のキーのインターフェースと基本的に違いはありません-キーで開くか、カードで開くことができます。



そして、Keyインターフェイスを含むMagnetic Cardクラスを作成します。 これを行うには、磁気キーカードを継承します。 しかし、インターフェースとともに、実装も継承されました。Openメソッドでは、Insert、Rotate、およびRemoveメソッドを呼び出します。これは、Magnetic Cardにはまったく不適切です。



Insert、Post、Removeシーケンスを使用して、Openメソッドの実装を磁気カードに最小限に抑えます。 Keyクラスの実装の詳細がわからないので、これはすでに悪いです。突然、実行する必要のある非常に重要なデータの変更を見逃してしまい、Key :: Openメソッドで行われたのですか? キーの内部実装にアクセスして、何をどのように確認する必要があります-そのような技術的能力(永久にオープンソースなど)を持っている場合でも、これは大きなカプセル化違反であり、良い結果にはつながりません。



それはまさにGamma et al。Write: 継承はカプセル化の違反です



このような質問については、自分で検討してみてください。

-キーが単にウェルに挿入され、磁気カードが常に上にあるという事実(中央ではなく下から)をどうすればよいですか?

-非接触カードを作成する必要がある場合はどうすればいいですか?



この問題への正しいアプローチは実装からインタフェースを切り離すことです。 多くの現代言語には、このための特別な構文があります。 下位言語では、たとえば、Tse-two-crosses-純粋に抽象的なクラスと多重継承(わいせつな語彙はごめん-歌から言葉を捨てない)でハックを使用できます。



クラスではなく、インターフェイスに依存する必要があります。



Openメソッドを含むKeyインターフェイスを宣言します。



Insert、Rotate、およびRemoveメソッドを使用して、Keyインターフェイスを実装するSwivel Keyクラスを宣言します。



磁気カードクラスを宣言します。これは、キーインターフェイスも実装しますが、独自の方法で-ロータリーキーの実装との不快な交差がありません。 これにより、インターフェイスを実装から分離することができました。



一般的な原則を常に覚えておいてください。クラスではなく、インターフェイスを使用する必要があります。 回転キーや磁気カードなど、これが何であるかは重要ではありません。ドアを開けられることが重要です。 つまり、オブジェクトの性質について考えるのではなく、オブジェクトの使用方法について考えています。



あなたには奇妙に思えるかもしれませんが、 これはまさに人と動物を区別するものです -クラスの代わりにインターフェースを使用します。 おそらく、バケツの水で火を消すように訓練された猿の古典的な経験を覚えているでしょう。 そして、バケツをプールの真ん中のいかだに乗せましたが、猿はまだプールから水を直接すくう代わりに、いかだへの橋に沿って走り、バケツから水をすくいました。 つまり、サルはWaterインターフェースの代わりにWater-in-Bucketクラスを使用しました(さらに、秘密を言います:Means-for-Extinguishingインターフェースの代わりに)。



クラスで考えると、動物のようになります。 人々はインターフェースで考える(そしてプログラムする)。



インターフェイスを使用すると、大きなチャンスが得られます。 たとえば、クラスは複数のインターフェイスを実装できます。Key-to-Doorphoneクラスには、KeyおよびFobインターフェイスを含めることができます。



あなたが思い出すように、カプセル化に違反するクラス継承に関しては、多くの場合、継承の代わりに委任と構成を使用する方良いです。 非接触型カードを忘れないでください? だから私は彼女を磁気カードに似せたい! (たとえば、両方ともウォレットのカード間スロットに入れることができることを知るために。)しかし、それらは共通点を持たないようです。キーインターフェースは、インターコムキーを備えたロータリーキーと同程度に関連しています。 。



解決策? クラス(またはインターフェイスかもしれません-ここでの方が良いと思います)磁気カードまたは非接触カードに委任することにより、キーインターフェイスを実装するカードを作成します。 そして、委任と構成が何であるか、そして抽象ファクトリーがそれとどう関係するのかを知るには、デザインパターンに関する本を参照してください。



クラスの代わりにインターフェイスを使用すると、さらに多くの利点があります。 あなたは実際にそれらを見ることができます。 人を止めろ!



PS驚いたことに、Habrahabrには、OOP専用のブログも、一般的なプログラミング専用のブログもありませんでした。 それらが存在する場合、それを示してください、しかし、今のところ、最良のオプションがない場合、私はそれらを個人的なブログに投稿します。



All Articles