インターフェイスとクラスの違いは何ですか?また、クラスにメソッドを直接実装できるときにインターフェイスを使用する必要があるのはなぜですか?


117

これは非常に基本的な質問であることは承知していますが、面接官が非常に巧妙な方法で私に尋ねたので、私は無力でした:(

私はインターフェースの重要なまたは理論的な定義のみを知っており、私が取り組んだ多くのプロジェクトにそれを実装しました。しかし、これがなぜ、どのように役立つのか、本当にわかりません。

インターフェースについても理解できません。つまり、たとえば、

conn.Dispose();ついにブロック。しかし、そのクラスがIDisposableインターフェイス(SqlConnection)クラスを実装または継承していることはわかりません。どうすればメソッド名を呼び出すことができるのでしょうか。また、同じことですが、Disposeメソッドがどのように機能するのか理解していません。すべてのインターフェイスメソッドに対して、独自の実装で関数本体を実装する必要があるためです。それでは、インターフェースはどのように受け入れられ、契約として命名されますか?これらの質問は今まで心の中にあり続けていましたが、率直に言って、自分の質問を理解できる形で説明する良いスレッドは見たことがありません。

MSDNはいつものように非常に恐ろしく見え、1行も明確ではありません(ハイレベルの開発に携わっている皆さん、親切に言い訳をしてください。コードや記事は、それを見る人の心に届くはずなので、他の多くの人が言うように、MSDN使用されません)。

インタビュアーは言った:

彼には5つのメソッドがあり、クラスに直接実装して喜んでいますが、抽象クラスまたは抽象インターフェースを使用する必要がある場合、どちらを選択するのか、そしてその理由は何ですか。抽象クラスとインターフェースの両方の長所と短所についてさまざまなブログで読んだすべてのものに彼は答えましたが、彼は確信していません、彼は一般的に「なぜインターフェース」を理解しようとしています。「なぜ抽象クラス」は、同じメソッドを1回だけ実装でき、それを変更しない場合でも、一般に。

ネットのどこにも、インターフェースとその機能について明確に説明してくれる記事はありませんでした。私はそれらの多くのプログラマーの1人ですが、まだインターフェースについては知りませんが(私は使用した理論と方法を知っています)、明確に理解していることに満足していません。


4
インターフェースも私が理解するのに苦労したものです。良い質問。
ブライアン

4
具体的な実装ではなく、抽象的なコントラクトへのプログラミング....つまり、インターフェイスが必要なときに、インターフェイスを実装するオブジェクトを置き換えることができます。
ミッチウィート

7
SqlConnectionSystem.ComponentModel.Componentどちらを実装するかを継承しますIDisposable
Lee

2
@MitchWheat-これは例ではありません。質問では、SqlConnection実装方法を尋ねますIDisposable
Lee

ああ、リー、ありがとうございます。しかし、「Dispose」メソッドの機能がどこでどのように定義されているかはまだわかりません。
学習者

回答:


92

そのようなものを作成したい場合、インターフェースは優れています:

using System;

namespace MyInterfaceExample
{
    public interface IMyLogInterface
    {
        //I want to have a specific method that I'll use in MyLogClass
        void WriteLog();       
    }

    public class MyClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyClass was Logged");
        }
    }

    public class MyOtherClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyOtherClass was Logged");
            Console.Write("And I Logged it different, than MyClass");
        }
    }

    public class MyLogClass
    {
        //I created a WriteLog method where I can pass as a parameter any object that implements IMyLogInterface.
        public static void WriteLog(IMyLogInterface myLogObject)
        {
            myLogObject.WriteLog(); //So I can use WriteLog here.
        }
    }

    public class MyMainClass
    {
        public void DoSomething()
        {
            MyClass aClass = new MyClass();
            MyOtherClass otherClass = new MyOtherClass();

            MyLogClass.WriteLog(aClass);//MyClass can log, and have his own implementation
            MyLogClass.WriteLog(otherClass); //As MyOtherClass also have his own implementation on how to log.
        }
    }
}

私の例では、私はを書く開発者MyLogClassであり、他の開発者はクラスを作成することができ、彼らがログを記録したいとき、彼らはインターフェースを実装しますIMyLogInterface。それは彼らが私にWriteLog()メソッドを使用するために実装する必要があるものを私に尋ねていたのと同じですMyLogClass。彼らがインターフェースで見つける答え。


3
こんにちは、私が理解するのに非常に良い材料のように見えます。本当に感謝しています。どうもありがとう:) :)
学習者

14
私の質問は、あなたがインスタンス化MyClassしているのMyOtherClassか、そしてなぜaClass.WriteLog()その追加のステップを追加するのかを単に呼ばないのかということです。の実装はWriteLog()クラスごとに異なりますが、すでにオブジェクトを持っているので、なぜそれをハンドラークラスに渡すのでしょうか。
Zach M.

おそらく、ログの例をナゲットに置くと、詳細を知らなくても、他の人がロガーを使用する方が簡単になるかもしれません。ロギングおよびアラートレベルのインターフェース)インターフェースは、まだスコープ内にのみ存在します。だから誰のためにそれから利益を得ているのか。
user3800527

2
@ZachM。私が正しい場合、答えは、彼はクラスをインスタンス化しないことを意味しますが、他の開発者はクラスをインスタンス化し、それをパラメーターとしてMyLogClass WriteLogメソッドに渡します。したがって、彼のメソッドはを実装する任意のオブジェクトを処理できますIMyLogInterfaceここに別の興味深い記事があります。
shaijut

1
私の質問は、なぜインターフェースですか?上記のシナリオは、すべての抽象メソッドを持つ抽象クラスによっても実現できます。
Abhijit Ojha

52

私がインターフェースを使用する理由の1つは、コードの柔軟性を高めるためです。次のように、パラメーターとしてクラス型Accountのオブジェクトを受け取るメソッドがあるとします。

public void DoSomething(Account account) {
  // Do awesome stuff here.
}

この問題は、メソッドパラメータがアカウントの実装に向けて修正されていることです。他のタイプのアカウントが必要ない場合は、これで問題ありません。この例では、アカウントインターフェイスをパラメーターとして使用しています。

public void DoSomething(IAccount account) {
  // Do awesome stuff here.
}

このソリューションは実装に向けて修正されていません。つまり、SuperSavingsAccountまたはExclusiveAccount(両方ともIAccountインターフェースを実装)を渡して、実装されたアカウントごとに異なる動作を取得できます。


45

インターフェースは、実装者が従わなければならないコントラクトです。抽象クラス使用すると、コントラクトと共有実装を実現できます。インターフェイスにはありません。クラスは、複数のインターフェースを実装および継承できます。クラスは、単一の抽象クラスのみを拡張できます。

なぜインターフェース

  • デフォルトまたは共有コードの実装がありません
  • データコントラクト(Webサービス、SOA)を共有したい
  • インターフェースの実装者ごとに異なる実装があります(IDbCommandSqlCommandありOracleCommand、特定の方法でインターフェースを実装しています
  • 多重継承サポートしたい。

なぜ抽象的か


2
@シルバー私はあなたがブログでタイプしたもののほとんどを読みましたが、私は実際に理解しようとしています。私はWCFサービスを実行し、インターフェイスを公開しました(ただし、これは単一のスタンドアロンアプリであり、アップストリームもダウンストリームもありません)。そのため、インターフェースを十分に設計および実装しましたが、適切に理解できませんでした。私の質問は、実際には、契約名が正しいというメソッド名を共有するだけですか??これはどのように私はすべてのメソッドを実装するだけの力を知っている:(便利ですが、他のどのインターフェイスで上記のあなたの投稿で、2点目はシェアを言い、手段は、あなたがこのの実用的なリアルタイムの例を与えることができます
学習

1
インターフェイスとSOAに関する実際的な例として、.NETアセンブリ(例:Contracts.Shared.dll)でWCFインターフェイスDataContracts)を共有し、 .NETクライアントのコンシューマーが(サービス参照の追加などによるコードの生成を避けて)簡単に相互運用できるようにします)または、共有タイプでサービス参照追加するChannelFactory
SliverNinja-MSFT

抽象クラス内で抽象メソッドのみを宣言する場合、抽象クラスがインターフェースとして機能するのに、なぜインターフェースが必要なのでしょうか。
Abhijit Ojha

25

ここに画像の説明を入力してください

したがって、この例では、PowerSocketは他のオブジェクトについて他に何も知りません。オブジェクトはすべて、PowerSocketによって提供されるPowerに依存しているため、IPowerPlugを実装し、そうすることでそれに接続できます。

インターフェースは、オブジェクトが互いに他のことを何も知る必要なく連携して動作するために使用できるコントラクトを提供するので便利です。


これは理にかなっていますが、私はまだ理解するのに苦労しています。PowerSocketの基本クラスを作成せず、他のすべてのものは必要に応じてそれを継承できますか?技術的には、Power Socketは他のクラスについて何も知りません。
altaaf.hussein

C#では多重継承が許可されていないためだと思います
Hugh Seagraves

22

一言で言えば、ポリモーフィズムのためです!

「実装ではなくインターフェースをプログラムする」場合は、同じインターフェース(タイプ)を共有する異なるオブジェクトを引数としてメソッドに注入できます。このように、メソッドコードは別のクラスの実装と結合されません。つまり、同じインターフェイスの新しく作成されたオブジェクトを操作するために常に開いています。(開閉原理)

  • Dependency Injectionを調べ、GOFによるDesign Patterns-Elements of Reusable Object-Oriented Softwareを必ずお読みください。

4

C#にはダックタイピングがありません。特定のメソッドが一連の具象クラスに実装されていることがわかっているからといって、そのメソッドの呼び出しに関してすべて同じように処理できるわけではありません。インターフェイスを実装すると、そのインターフェイスが定義するものに関して、それを実装するすべてのクラスを同じタイプのものとして扱うことができます。


3
動的タイプを使用すると、.net4で一種のダックタイピングを取得できます。
Tony Hopkinson、

4

私はこの質問をするときにすでに多くの血が流出していると思います、そして多くの人は通常の人間が理解できないロボットのような用語を説明することによってこの問題を解決しようとします。

だからまず。なぜインターフェースなのか、なぜ抽象的なのかを学ぶには、それらの目的を学ぶ必要があります。ファクトリークラスを適用するときに私は個人的にこの2つを学びました。あなたはこのリンクで良いチュチュアルを見つけます

今、私がすでに与えたリンクに基づいて掘り下げましょう。

あなたは持っている車両(ユーザーの要件に変更される可能性があるクラスを追加するようにトラック戦車飛行機などを、そして与えられた私たちがしています

public class clsBike:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Bike");
    }
    #endregion
}

そして

public class clsCar:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Car");
    }
    #endregion
}

両方とも契約IChoiceを持っています

public interface IChoice
{
    string Buy();
}

ご覧のとおり、そのインターフェースはメソッドを強制するだけですBuy()が、継承したクラスがそれを実装するときに何をするかを決定させます。これはインターフェースの制限であり、純粋なインターフェースを使用すると、abstactを使用して自動的に実装できるタスクを繰り返す可能性があります。この例では、各車両の購入には割引があります。

public abstract class Choice
{
    public abstract string Discount { get; }
    public abstract string Type { get; }
    public string Buy()
    {
       return "You buy" + Type + " with " + Discount;
}
public class clsBike: Choice
{
    public abstract string Discount { get { return "10% Discount Off"; } }
    public abstract string Type { get { return "Bike"; } }
}

public class clsCar:Choice
{
    public abstract string Discount { get { return " $15K Less"; } }
    public abstract string Type { get { return "Car"; } }
}

ファクトリクラスを使用すると、同じことを実現できますが、抽象を使用すると、基本クラスにBuy()メソッドを実行させることができます。

要約:インターフェイスコントラクトは継承クラスに実装を許可しますが、抽象クラスコントラクトは実装を初期化できます(継承クラスによってオーバーライドできます)。


2

インターフェイスを使用すると、次のことができます。

1)実装の異なるカットを提供する分離されたインターフェースを作成し、よりまとまりのあるインターフェースを可能にします。

2)インターフェース間で同じ名前の複数のメソッドを許可します。これは、競合する実装がなく、シグニチャのみであるためです。

3)実装に関係なく、インターフェースのバージョンを設定してハイブを解除し、契約が確実に満たされるようにすることができます。

4)コードは、コンクリエーションではなく抽象化に依存できるため、テストモックの注入など、スマートな依存関係の注入が可能になります。

私が確信している理由は他にもたくさんありますが、これらはほんの一部です。

抽象クラスを使用すると、部分的に具体的なベースを使用できます。これはインターフェースと同じではありませんが、テンプレートメソッドパターンを使用して部分的な実装を作成する機能など、独自の品質を備えています。


最も重要なことはおろそかにしました。インターフェイスを実装すると、そのインターフェイスの実装を必要とするすべてのコードでクラスを使用できるようになり、問題のコードはクラスについて何も知る必要がありません。
スーパーキャット

2

@ user2211290回答の実際のサンプルとして:

両方ArrayListのインタフェースを持っていますIList。以下にa string[]とaがList<string>あり、両方を1つの方法で使用して確認します IListます

string[] col1 = { "zero", "one", "two", "three", "four"};
List<string> col2 = new List<string>{ "zero", "one", "two", "three"};

//a methode that manipulates both of our collections with IList
static void CheckForDigit(IList collection, string digit)
{
    Console.Write(collection.Contains(digit));
    Console.Write("----");
    Console.WriteLine(collection.ToString()); //writes the type of collection
}

static void Main()
{
    CheckForDigit(col1, "one");   //True----System.String[]
    CheckForDigit(col2, "one");   //True----System.Collections.Generic.List`1[System.String]



//Another test:

    CheckForDigit(col1, "four");   //True----System.String[]
    CheckForDigit(col2, "four");   //false----System.Collections.Generic.List`1[System.String]
}

1

継承できる抽象クラスは1つだけです。複数のインターフェースから継承できます。これにより、ほとんどの場合に使用するものが決まります。

抽象クラスの利点は、基本的な実装が可能なことです。ただし、IDisposableの場合、基本クラスは適切にクリーンアップする方法を知らないため、デフォルトの実装は役に立ちません。したがって、インターフェースの方が適しています。


1

抽象クラスとインターフェースはどちらもコントラクトです。

コントラクトのアイデアは、いくつかの動作を指定することです。実装したと言えば、契約に同意したことになります。

インターフェースよりも抽象を選択することです。

抽象クラスの非抽象子孫はコントラクトを実装します。

インターフェースを実装するクラスはすべてコントラクトを実装します。

したがって、すべての子孫が実装して別のインターフェースを定義して保存する必要がある動作を指定する場合は、抽象を使用しますが、この効果的に集約されたコントラクトを満たすすべてが子孫である必要があります。


1

インターフェイスは、現実(オブジェクト)の抽象(クラス)の抽象(アーキタイプ)を作成するためのものです。

インターフェイスは、クラスによって提供される実装を提供することなく、契約条件を指定することです。

インターフェイスは仕様です:

  • インターフェースは、それが単独で静的であるため、概念の不動の動作を指定する設計時の成果物です。

  • クラスは実装時のアーティファクトであり、相互作用して移動する現実のモバイル構造を指定します。

インターフェースとは?

猫を観察すると、足、頭、体幹、尻尾、髪の毛が4つある動物だと言えます。彼が歩く、走る、食べる、鳴くことができることがわかります。等々。

これで、プロパティとその操作を含むインターフェースが定義されました。このように、手口は定義していませんが、機能のしくみを知らない機能と能力のみを定義しています。能力と区別を定義しています。

そのため、UMLではクラス図でこれをクラスと呼びますが、実際にはまだクラスではありません。プライベートと保護されたメンバーを定義してアーティファクトの深いビューを開始できるためです。UMLのインターフェースはC#のインターフェースとは少し異なるため、ここで混乱しないでください。これは、抽象化アトムへの部分的なアクセスポイントのようなものです。そのため、クラスは複数のインターフェースを実装できると述べました。C#のインターフェイスはどちらも抽象化を抽象化するためと、この抽象化をアクセスポイントとして制限するために使用されるため、同じことです。それは2つの異なる用途です。したがって、UMLのクラスはプログラミングクラスへの完全な結合インターフェースを表し、UMLインターフェースはプログラミングクラスのセクションの分離インターフェースを表します。確かに、UMLのクラス図は実装を処理せず、そのすべてのアーティファクトはプログラミングインターフェイスレベルにあります。UMLクラスをプログラミングクラスにマッピングしますが、これは抽象抽象化から具象抽象化への転置です。デザインの分野とプログラミングの分野の二分法を説明する微妙な点があります。したがって、UMLのクラスは、内部の隠されたものを考慮しながら、プログラミングインターフェイスの観点から見たプログラミングクラスです。

インターフェースは、厄介な方法で利用できない場合に、複数の継承をシミュレートすることもできます。たとえば、猫クラスは、動物インターフェースから派生する猫インターフェースを実装します。この猫クラスは、これらのインターフェースも実装します:歩く、走る、食べる、音を出す。これにより、クラスレベルでの多重継承の欠如が補われますが、毎回すべてを再実装する必要があり、現実そのもののように現実をうまく分解することはできません。

ユニットでインターフェースと実装セクションを定義するPascalオブジェクトコーディングを参照できることを理解するために。インターフェースで型を定義し、実装で型を実装します。

unit UnitName;

interface

type
  TheClass = class
  public
    procedure TheMethod;
  end;

implementation

class procedure TheClass.TheMethod;
begin
end;

ここでは、インターフェイスセクションはUMLクラスの設計と一致していますが、インターフェイスタイプは他のものです。

したがって、私たちのビジネスでは、2つの異なるが類似したものを指名するために、インターフェースという1つの単語があり、それが混乱の原因となっています。

また、たとえばC#では、プログラミングインターフェイスを使用すると、厳密に型指定されたhabilityを失ったために、目標を実際に達成することなく、オープンタイプの真の総称多態性の欠如を補正できます。

結局のところ、互換性のないシステムが(分散型)共通オブジェクトモデルで導入されたようなメモリ内のオブジェクトの実装と管理を心配せずに通信できるようにするためのインターフェイスが必要です。

クラスとは?

外部の視点から現実の縮小を定義した後、内部の視点からそれを説明できます。これは、カプセル化した現実を実現し、感謝の相互作用を可能にするデータ処理とメッセージ管理を定義するクラスですインスタンスを使用してオブジェクトに。

UMLでは、機械のホイールにフラクタルの没入感を実現し、状態、相互作用などを記述して、処理したい現実のフラグメントの抽象化を実装できるようにします。

そのため、抽象クラスは、コンパイラーの観点からは、どういうわけかインターフェースと同等です。

詳しくは

プロトコル(オブジェクト指向プログラミング)

C#-インターフェイス

C#-クラス


1

空飛ぶトースターについてお話ししましょう。

フライングトースター

もちろん、インターフェイスをまったく宣言も実装もせずに機能するソフトウェアシステムを構築できる状況は数多くあります。オブジェクト指向のソフトウェア設計は、クラスだけを使用して実現できます。

さらに、任意のソフトウェアシステムをアセンブリ言語で実装することも、マシンコードで実装することもできます。抽象化メカニズムを使用する理由は、抽象化メカニズムが物事を容易にする傾向があるためです。インターフェースはそのような抽象化メカニズムです。

そのため、インターフェースを使用すると実装が非常に簡単になる特定の重要なオブジェクト指向設計があり、そのような場合にインターフェースが実際に必要になることがあります。

これらの重要な設計は多重継承に関係しています。多重継承は、「真の」形式では、クラスが1つの基本クラスだけでなく、2つ以上の基本クラスから継承する場合です。この真の形式はC#では不可能ですが、C#やJavaなどの言語が登場する前は、C ++が支配していた言語であり、真の多重継承を完全にサポートしています。残念ながら、真の多重継承は言語の設計を非常に複雑にし、また有名な「ダイヤモンド問題」などのさまざまな問題を引き起こすため、あまり良い考えではないことがわかりました。(Jフランシスの「多重継承の正確な問題は何ですか?」の回答を参照してください)

したがって、誰かが「フライングトースター」クラスを構築したい場合、既存の「トースター」クラスと既存の「フライング」クラスから継承します。彼らが遭遇する可能性があった種類の問題は、トースタークラスの電源が壁のコンセントである可能性が高い一方で、飛行機械クラスの電源がピジョンフードである可能性が高いことであり、結果として生じる新しいクラスはどういうわけか両方を持っているか、どちらが持っているかは不明です。(ダイヤモンドの問題。)

C#やJavaなどの言語の作成者は、言語を単純に保ち、Diamond Problemのような落とし穴を回避するために、真の多重継承を許可しないことに決めました。ただし、何らかの形式の多重継承が依然として必要である(または少なくとも非常に望ましい)ため、これらの言語では、真の多重継承の問題と複雑さを回避しながら、より少ない形式の多重継承をサポートする手段としてインターフェイスを導入しました。

この小さい形式の多重継承では、複数の基本クラスから継承するクラスを持つことはできませんが、少なくとも1つ以上のインターフェースから継承することはできます。したがって、フライングトースターを作成する場合、既存のトースタークラスと既存のフライングクラスの両方から継承することはできませんが、既存のトースタークラスから継承して、自分で実装するフライングインターフェイスを公開することもできます。おそらく、トースターからすでに継承している手段を使用します。

したがって、2つの異なる関連のない機能のセットを集約するクラスを作成する必要性を感じない限り、インターフェイスを宣言または実装する必要がないため、多重継承の形式は必要ありません。


0

インターフェイスを使用すると、クラスの設計者は使用可能なメソッドをエンドユーザーにわかりやすくすることができます。それらはまた、ポリモーフィズムの不可欠な部分です。


よくあなたの最初の声明で言った。しかし、私はあなたの2番目のステートメントを理解しています。リアルタイムの例で詳しく説明していただけませんか?
学習者

0

私はあなたが理論をよく知っていると思いますし、SOLIDの原則を知っていると思いますので、実践に移りましょう。

ご存じのとおり、インターフェースにはコードを含めることができないため、利点は非常に簡単に理解できます。

コンストラクターを提供するクラスのプロパティを初期化する必要がある場合、または実装の一部を提供する場合は、抽象クラスがそれを許可しないインターフェイスに適しています。

そのため、非常に一般的には、クラスを継承/拡張するクライアントにコンストラクターまたはコードを提供する必要がある場合、インターフェースよりも抽象クラスを優先する必要があります。


-2

抽象クラスは関連エンティティ用に作成され、インターフェイスは関連のないエンティティに使用できます。

たとえば、AnimalとHumanの2つのエンティティがある場合、インターフェイスに行きます。詳細に行く必要があるかのように、Tiger、lionと言って、Animalと関連付けたい場合は、Animal Abstractクラスを選択します。

以下のようになります

   Interface             
   ____|____
  |        |
Animal   Human



  Animal (Abstract class)
   __|___
  |      |
Tiger   Lion
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.