web-dev-qa-db-ja.com

「インターフェースへのプログラミング」を理解する

「実装ではなくインターフェースへのプログラミング」という言葉をよく目にしましたが、それが何を意味するのかはある程度理解できたと思います。しかし、私はそれが利点であり、それが可能な実装であることを確実に理解したいと思います。

「インターフェースへのプログラミング」とは、可能な場合、具体的な実装を参照するのではなく、クラスのより抽象的なレベル(インターフェース、抽象クラス、またはある種のスーパークラス)を参照する必要があることを意味します。

Javaの一般的な例は、次のものを使用することです。

List myList = new ArrayList();の代わりにArrayList myList = new ArrayList();.

これに関して2つの質問があります。

  1. このアプローチの主な利点を確実に理解したいと思います。メリットはほとんどが柔軟性だと思います。オブジェクトを具体的な実装ではなく、より高レベルの参照として宣言すると、開発サイクル全体およびコード全体で、柔軟性と保守性が向上します。 これは正しいですか?柔軟性が主な利点ですか?

  2. 「インターフェイスへのプログラミング」の方法は他にありますか?あるいは、この概念の唯一の実装は、「具体的な実装ではなくインターフェースとして変数を宣言する」だけですか?

私はJavaインターフェースの構築)について話していませんです。OO原則 "インターフェイスへのプログラミング、この原則では、世界の「インターフェース」はクラスの「スーパータイプ」を指します-インターフェース、抽象クラス、またはより抽象的で具体性の低い単純なスーパークラスより具体的なサブクラスです。

32
Aviv Cohn

「インターフェースへのプログラミング」とは、可能な場合、具体的な実装を参照するのではなく、クラスのより抽象的なレベル(インターフェース、抽象クラス、またはある種のスーパークラス)を参照する必要があることを意味します。

これは正しくありません。または、少なくとも、それは完全に正しいわけではありません。

より重要な点は、プログラム設計の観点から来ています。ここで、「インターフェースへのプログラミング」とは、-方法ではなく、コードが実行しているwhatに設計を集中させることを意味します。これは、設計を正確さと柔軟性に向けて推進する重要な違いです。

主な考え方は、ドメインの変更がソフトウェアの変更よりもはるかに遅いということです。買い物リストを追跡するソフトウェアがあるとします。 80年代には、このソフトウェアはコマンドラインとフロッピーディスク上のいくつかのフラットファイルに対して機能しました。次に、UIを取得しました。次に、おそらくリストをデータベースに入れます。後でそれは多分クラウドか携帯電話またはfacebook統合に移動しました。

実装(フロッピーディスクとコマンドライン)を中心にコードを設計した場合、変更に対する準備が不十分になります。インターフェイス(食料品リストの操作)を中心にコードを設計した場合、実装は自由に変更できます。

50
Telastyn

「インターフェイスへのプログラミング」についての私の理解は、質問や他の回答が示唆するものとは異なります。私の理解が正しいとか、他の答えのものが良い考えではないということではありません。その用語を聞いたときに私が思うことではないのです。

インターフェイスへのプログラミングとは、プログラミングライブラリ(クラスライブラリ、関数のセット、ネットワークプロトコルなど)が提示されたときに、インターフェイスで保証されているものだけを使用し続けることを意味します。基礎となる実装についての知識はあるかもしれませんが(作成した可能性があります)、その知識を使用することはできません。

たとえば、APIが内部の何かへの「ハンドル」である不透明な値を提示するとします。このハンドルは実際にはポインタであることがわかると、ハンドルを逆参照して値にアクセスできるため、目的のタスクを簡単に実行できる場合があります。しかし、インターフェースはそのオプションを提供しません。特定の実装についての知識です。

これの問題は、それがあなたのコードと実装との間の強い結合を作り出すことであり、まさにインターフェースが防ぐはずのものでした。政治に応じて、コードを壊すために実装を変更できなくなるか、コードが非常に脆弱であり、基礎となる実装のアップグレードまたは変更のたびに壊れ続ける可能性があります。

この大きな例は、Windows用に作成されたプログラムです。 WinAPIはインターフェイスですが、多くの人々は、Windows 95などの特定の実装のために機能するトリックを使用していました。これらのトリックにより、プログラムが高速化したり、通常よりも少ないコードで実行したりできるようになります。ただし、これらのトリックは、APIの実装方法が異なるため、プログラムがWindows 2000でクラッシュすることも意味していました。プログラムが十分に重要である場合、マイクロソフトは実際にプログラムを引き続き機能させるために実装にいくつかのハックを追加する可能性がありますが、そのコストはWindowsコードの(その後のすべての問題により)複雑さが増します。また、WinAPIも実装しようとするため、Wineの人々の生活が非常に困難になりますが、その方法についてはドキュメントを参照するだけで、多くのプログラムが(誤ってまたは意図的に)実装の詳細に依存します。

19
Sebastian Redl

私の個人的な経験についてのみ話すことができます。これも正式に教えられたことがないためです。

あなたの最初のポイントは正しいです。柔軟性が得られたのは、具体的なクラスの実装の詳細を誤って呼び出すことができない場所で誤って呼び出せないためです。

たとえば、現在具象ILoggerクラスとして実装されているLogToEmailLoggerインターフェースについて考えてみます。 LogToEmailLoggerクラスは、すべてのILoggerメソッドとプロパティを公開しますが、実装固有のプロパティsysAdminEmailも持っています。

ロガーがアプリケーションで使用されている場合、sysAdminEmailを設定するのにコードを使用する心配はありません。このプロパティはロガーのセットアップ中に設定する必要があり、世界から隠す必要があります。

具体的な実装に対してコーディングを行っている場合、ロガーを使用するときに実装プロパティを誤って設定する可能性があります。これで、アプリケーションコードはロガーに密接に結合され、別のロガーに切り替えるには、最初に元のコードからコードを分離する必要があります。

この意味で、インターフェイスへのコーディング結合を緩める

2番目の点について:インターフェースへのコーディングで私が見たもう1つの理由は、コードの複雑さを軽減することです。

たとえば、次のインターフェイスI2DRenderableI3DRenderableIUpdateableのゲームがあるとします。 1つのゲームコンポーネントが2Dと3Dの両方のレンダリング可能なコンテンツを持つことは珍しくありません。他のコンポーネントは2Dのみで、他のコンポーネントは3Dのみです。

2Dレンダリングが1つのモジュールで行われる場合、I2DRenderablesのコレクションを維持することは理にかなっています。コレクション内のオブジェクトがI3DRenderableまたはIUpdatebleであるかどうかは関係ありません。他のモジュールがオブジェクトのこれらの側面を処理するためです。

レンダリング可能なオブジェクトをI2DRenderableのリストとして格納すると、レンダリングクラスの複雑さが低く抑えられます。 3Dレンダリングと更新ロジックは関係ありません。そのため、子オブジェクトのこれらの側面は無視でき、無視する必要があります。

この意味で、インターフェイスへのコーディングは問題の切り分けによって複雑さを低く抑えます。

9
MetaFight

ここで使用されているWordインターフェイスには、おそらく2つの使用法があります。質問で主に参照しているインターフェースは Javaインターフェース です。具体的には、Java=コンセプト、より一般的にはプログラミング言語のインターフェースです。

インターフェースへのプログラミングはより広い概念だと思います。現在人気のあるREST多くのWebサイトで利用可能なAPIは、上位レベルのインターフェイスへのプログラミングのより広い概念のもう1つの例です。コードの内部動作と外部の間にレイヤーを作成することにより世界(インターネット上の人々、他のプログラム、同じプログラムの他の部分)は、外部の世界が期待しているものを変更しない限り、コード内のあらゆるものを変更できます。あなたが名誉を与えるつもりであること。

これにより、インターフェースに依存する他のすべてのことを伝える必要がなく、内部コードをリファクタリングする柔軟性が得られます。

また、コードがより安定している必要があります。インターフェイスに固執することで、他の人のコードを壊してはいけません。本当にインターフェースを変更する必要がある場合は、新しいバージョンのインターフェースに重大な変更があることを知らせるAPIの新しいメジャーバージョン(1.a.b.cから2.x.y.z)をリリースできます。

@Dovalがこの回答のコメントで指摘しているように、エラーの局所性もあります。これらは、すべてカプセル化に要約されると思います。オブジェクト指向デザインのオブジェクトに使用するのと同じように、このコンセプトは上位レベルでも役立ちます。

4
Encaitar

現実世界の例えが役立つかもしれません:

主電源はインターフェースにプラグインします。
はい;テレビ、ラジオ、掃除機、洗濯機などの電源コードの端にある3つのピンで留められたもの。

メインプラグを持つすべてのアプライアンス(つまり、「メインプラグを持つ」インターフェースを実装)は、正確にで同じ方法で処理できます。それらはすべて壁のソケットに差し込むことができ、そのソケットから電力を引き出すことができます。

個々のアプライアンス行うはまったく異なります。テレビでカーペットを掃除するのはそれほど遠くなく、ほとんどの人は娯楽のために洗濯機を見ません。しかし、これらのアプライアンスはすべて、壁のコンセントに接続できるという共通の動作を共有しています。

それが、インターフェースがもたらすものです。
nified継承の複雑化を必要とせずに、オブジェクトの異なるクラスmanyによって実行できる動作。

4
Phill W.

「インターフェースへのプログラミング」という用語は、多くの解釈に開かれています。ソフトウェア開発におけるインターフェースは非常に一般的な言葉です。ここに、私が長年にわたって訓練したジュニア開発者にコンセプトを説明する方法があります。

ソフトウェアアーキテクチャには、さまざまな自然境界があります。一般的な例は次のとおりです

  • クライアントとサーバーのプロセス間のネットワーク境界
  • アプリケーションとサードパーティライブラリ間のAPI境界
  • プログラム内の異なるビジネスドメイン間の内部コード境界

重要なのは、これらの自然な境界が存在する場合、それらが識別され、その境界がどのように動作するかの規約が指定されることです。 「反対側」が動作するかどうかではなく、相互作用が仕様に一致するかどうかでソフトウェアをテストします。

この結果は次のとおりです。

  • 外部コンポーネントは、仕様を実装している限り交換できます
  • 正しい動作を検証するための単体テストの自然なポイント
  • 統合テストが重要になります-仕様があいまいでしたか?
  • 開発者は、特定のタスクに取り組むときに懸念の世界が狭くなります。

これの多くはクラスとインターフェースに関連する可能性がありますが、データモデル、ネットワークプロトコル、およびより一般的な方法で複数の開発者との作業にも関連することを理解することも同様に重要です

1
Michael Shaw