3

C# やその他の OOP 言語のインターフェイスをよりよく理解しようとしています。インターフェイスは何をしますか? なぜそれが必要なのですか?私は、C# と Java が多重継承を許可していないことを知っています。ほとんどの本では、インターフェイスは単一継承の制限を回避し、異なるクラスが共通の機能を持つことを可能にする 1 つの方法であると述べています。インターフェイスはメソッドを定義するだけで、クラスにそれらを実装するように強制します。インターフェイスを扱わずに、クラス自体でメソッドを定義および実装しないのはなぜですか? 例えば:

4: using System;
5:
6: public interface IShape
7: {
8:    double Area();
9:    double Circumference();
10:   int Sides();
11: }
12:
13: public class Circle : IShape
14: {
15:    public int x;
16:    public int y;
17:    public double radius;
18:    private const float PI = 3.14159F;
19:
20: public double Area()
21: {
22:    double theArea;
23:    theArea = PI * radius * radius;
24:    return theArea;
25: }
.
.
.

Circle クラスで Area()、Circumference()、Sides() メソッド自体を定義および実装できないのはなぜですか? Square クラスが IShape を継承する場合、Circumference() メソッドは実装されていない必要があります。インターフェースの理解が途方に暮れていますか?

4

10 に答える 10

5

インターフェースは、「あなたがどのようにやっているかは気にしませんが、これがあなたがやり遂げる必要があることです」と言いたいときのためのものです。詳細については、このリンクを参照してください。

于 2012-09-10T04:55:48.720 に答える
3

C# のオブジェクトには、実際にはさまざまなカテゴリの複合体である関数が含まれる場合があります。これの古典的な例は教師の例です:

多重継承

この例では、教師は個人の特徴 (目の色など) (ただし、この例を破る可能性のある教師が何人かいます) と従業員の特徴 (給与など) を持っています。

C# では多重継承が許可されていないため、代わりにインターフェイスを使用した構成のアイデアに注目しています。多くの場合、次のように説明されます。

継承とは、Cat が Animal から継承する場合、Cat は「動物」であることを意味します。

構成は、Cat が Noise を実装する場合、Cat が Noise を「持つ」ことを意味します。

この区別が重要なのはなぜですか。さて、私たちの猫を想像してみてください。実際には、Cat に Feline を継承させ、さらに Feline に Animal を継承させることができます。ある日、他のタイプのアニマルをサポートすることを決定したため、アニマルを改訂することにしましたが、その後、これらの変更を他のすべてのサブタイプにプッシュすることに気付きました。私たちの設計と階層は突然非常に複雑になり、最初からうまくいかなかった場合は、すべての子クラスを大幅に再設計する必要があります。

ルールはInheritanceよりも合成を優先することですが、優先という言葉の使用に注意してください。これは重要です。これは、単に合成を使用する必要があるという意味ではなく、継承が有用な場合と合成が有用な場合を設計で考慮する必要があるためです。それも。また、可能なすべてのメソッドを基本クラスに固執するのは悪い考えであることも思い出させてくれます。

あなたの形の例が好きです。検討:

小型形状選別機

ShapeSorter には、次のようなメソッドが含まれる場合があります。

public bool Sort(Shape shape)
{
  foreach(Hole hole in Holes)
  {
     if(Hole.Type == HoleType.Circular && shape is Circle)
     {
         return true;
     }
     if(Hole.Type == HoleType.Square && shape is Square)
     {
         return true;
     }
     if(Hole.Type == HoleType.Triangular && shape is Triangle)
     {
         return true;
     }
  }

  return false;

}

または、制御を少し反転させることもできます。

public bool Sort(Shape shape, Hole hole)
{
   return hole.Accepts(shape); //We  end up pushing the `if` code into Hole
}  

またはその変形。私たちはすでに、Shape の正確な種類を知っていることに依存する多くのコードを書いています。次のいずれかがある場合、維持するのがどれほど面倒になるか想像してみてください。

大型形状選別機

その代わりに、私たちは自分自身で考えます - 問題を関連する特性に絞り込むことによって、問題を説明できるより一般的な方法はありますか?

あなたはそれをIShapeと呼んだ:

public interface IShape{
  double Area {get;}
  double Perimeter { get; } //Prefer Perimeter over circumference as more applicable to other shapes
  int Sides { get; }
  HoleType ShapeType { get; }
}

次に、Sort メソッドは次のようになります。

public Hole Sort(IShape shape)
{
   foreach(Hole hole in Holes)
   {
      if(hole.HoleType == shape.ShapeType && hole.Area >= shape.Area)
      {
         return hole;
      }
   }
   return null;
}

これは見た目はすっきりしていますが、Shape を介して直接行うことができなかったわけではありません。

真実は、真実がないということです。最も一般的なアプローチは、継承と構成の両方を使用することです。現実世界の多くのものは型であり、インターフェイスによって最もよく説明される他の属性も持っているためです。避けるべき最も重要なことは、可能なすべてのメソッドを基本クラスに固執し、派生型ができることとできないことを解決する巨大な if ステートメントを用意することです。これは、維持するのが難しい多くのコードです。また、基本クラスに多くの機能を配置すると、コードが具体的に設定される可能性があります。すべての潜在的な副作用のために、後で修正する必要はありません。

于 2012-09-10T23:11:16.557 に答える
1

インターフェイスは、2つ以上のクラス間の相互作用中に使用されるコントラクトとも呼ばれます。

この場合、クラスが実装されていると言われるとIShape、呼び出し元は、クラスがと呼ばれるコントラクトで定義されているすべてのメソッドを持っていることを認識しIShapeます。Square呼び出し元は、クラスが//であるRectangleかどうかを心配する必要はありませんCircle

あなたの質問に答えるために

正方形のクラスがを継承する場合IShapeCircumference()メソッドは実装されていない必要があります。

十分に汎用的であるようにインターフェースを設計する必要があります。この場合、インターフェースには、円周ではなく周長と呼ばれるメソッドが必要です。

Sidesプロパティの場合、円は無限大を表すint.MaxValueなどの事前定義された定数を返す必要があると思います。

インターフェイスの利点を理解するには、呼び出し元がこれらのメソッドをどのように呼び出すかを確認する必要があります。

例えば

public double DisplayArea(IShape shape)
{
    Console.WriteLine(shape.Area().ToString());
}

上記のメソッドは、次の方法で呼び出すことができます。

//Code to create a Circle
this.DisplayArea(circle);

//Code to create a Square
this.DisplayArea(square);

これが可能なのは、タイプのオブジェクトには、を返すメソッドが呼び出されることDisplayAreaを知っているからです。したがって、クラス名はまったく気になりません。将来、クラスを実装してインターフェイスを実装するようにすると、メソッドは変更なしで機能します。IShapeAreadoubleEllipseIShapeDisplayArea

于 2012-09-10T05:04:00.107 に答える
1

Circle クラスで Area()、Circumference()、Sides() メソッド自体を定義および実装できないのはなぜですか?

それは可能ですが、インターフェースとは独立しています。インターフェイスが提供する主な利点の 1 つは、「ランタイム バインディング」です。

現在の例では、クラスが 1 つしかありません。Circle別のクラスを作成しRectangle、両方のクラスが実装しているとしますIShape。実行時のコードの後半で、 and のオブジェクトを作成しCircleRectangleそれをインスタンス型変数内に含めることができます。

IShape 形状;

if(User needs a Circle) // ランタイム バインディングを示すサンプル テストです。形状 = 新しい円 (); それ以外の形状 = new Rectangle();

今あなたがするとき

double area = shape.Area();

形状の面積を取得します。実行時には、エリアが必要なのRectangleCircle. ただし、インターフェイス オブジェクトは、参照を保持している形状の実装されたメソッドを呼び出します。

于 2012-09-10T05:01:58.583 に答える
1

インターフェイスは、オブジェクトが特定のメソッドまたはプロパティのセットを実装することを保証します。はい、シェイプ クラスはインターフェイスなしでこれらのメソッドを実装できますが、それらを実装していることを他のコードに伝える方法はなく、うっかり実装を忘れたり、パラメーターを変更したりする可能性があります。

インターフェイスは定義です。これにより、他のコードは、実装が何であるかを知らなくても、定義が何であるかを知ることができます。つまり、サークルがメソッドを実装できないのではなく、サークルがインターフェイスを実装する場合、メソッドを実装することが保証されるということです。

たとえば、IWriter というインターフェイスがあるとします。IWriter には 1 つのメソッドがあります。

public interface IWriter
{
    void Write(string s);
}

それがいかに一般的であるかに注意してください。何を書いているのか、どのように書いているのかはわかりません... 書いていることだけです。

次に、MemoryWriter、ConsoleWriter、PrinterWriter、HTMLWriter など、IWriter を実装する具体的なメソッドを作成できます。それぞれの書き込み方法は異なりますが、メソッドは 1 つだけで同じ単純なインターフェイスを実装しています。

public class ConsoleWriter : IWriter
{
    public void Write(string s) {
         Console.WriteLine(s);
    }
}

public class MemoryWriter : IWriter
{
    public void Write(string s) {
        // code to create a memory object and write to it
    }
}

ベースクラスで同じことを達成することもできますが、これは実際の実装に依存し、望ましくない依存関係を作成することになります。インターフェイスは、実装を定義から切り離します。

円周を持たない正方形と側面を持たない円について...それは、あなたの IShape 定義が適切に設計されていないことを意味します。適用できない可能性のある単一の定義にオブジェクトを適合させようとしています。

于 2012-09-10T05:02:41.267 に答える
1

現実の世界では、サークルだけでなく作業を行います。長方形/正方形などの他の形状が必要になります。インターフェイスを使用すると、アプリケーション内で使用する共通の契約を定義でき、新しい形状を簡単に追加できます。また、新しいシェイプを追加するときにコードを大幅に変更する必要はありません。IShape インターフェイスを実装するだけで完了です (いくつかの変更が必要になりますが、それほど多くはありません)。

于 2012-09-10T08:44:09.903 に答える
1

確かに、インターフェースを実装せずに、独自にメソッドをクラスに追加できます。しかし、インターフェースは、あなたの希望に任せることなく、確実にそれを行うことができます。これは、義務マイナス仲裁のようなものです。前述のように、インターフェースは履行しなければならない契約であり、どのように履行するかはあなた次第です。

于 2012-09-10T05:21:52.380 に答える
1

この場合、はい、間違いなくそれを行うことができます。しかし、mouse listenersどれがインターフェースであるかのようないくつかのケースがあります。それらで宣言されているダミーのメソッドがあります。後で開発者としてプログラムで、これらのメソッドをオーバーライドし、独自のカスタム ロジックを実装します。

Source : MouseListener インターフェイスは、mouseClicked、mouseEntered、mouseExited、mousePressed、および mouseReleased の 5 つのメソッドを定義します。これらはすべて void メソッドであり、単一の MouseEvent オブジェクトをパラメーターとして取ります。これら 5 つのメソッドすべてを定義する必要があることに注意してください。それ以外の場合は、クラスを抽象として宣言する必要があります。MouseListener インターフェイスは、マウスの明示的な動きなどのイベントを追跡しません。

于 2012-09-10T04:58:08.847 に答える