プログラムに実装できるようにインターフェイスを理解しようとしていますが、どのように使用すればよいか想像できません。また、C#で多重継承でそれらを使用する例を教えてください
9 に答える
インターフェイスの良い例は、リポジトリ パターンです。インターフェイスは、Get、GetAll、Update、Delete などのメソッドを定義します。実装はなく、関数シグネチャのみです。
次に、そのクラスの「具体的な」実装を記述して、たとえば MySQL で動作させることができます。ただし、UI はインターフェースのみを参照する必要があります。
後で Microsoft SQL に変更する場合は、別の具体的な実装を作成しますが、UI コードを (あまり) 変更する必要はありません。
C# には、1 つの「具体的な」クラスからしか継承できないという意味で、多重継承は存在しません。ただし、必要な数のインターフェイスを継承 (または「実装」) できます。
私はビデオゲームを書いています。このビデオ ゲームでは、ゲーム内のオブジェクトにさまざまな力を適用します。スラスト力、衝撃力、重力。計算方法は異なりますが、基本的な要素はすべて同じです。力を評価し、力がアタッチされているオブジェクトに力を追加する更新関数を呼び出す必要があります。
そこで、署名の更新機能を持つ IForce インターフェイスを作成しました。私の軍隊はすべてこのインターフェースを実装しています:
public interface IForce
{
void Update(Particle particle, GameTime gameTime);
}
これがサンプル実装です。
public class Spring : IForce
{
private Particle ThisParticle;
private Particle ThatParticle;
private float K;
public Spring(Particle thisParticle, Particle thatParticle, float k)
{
ThisParticle = thisParticle;
ThatParticle = thatParticle;
}
public void Update(Particle particle, GameTime gameTime)
{
float X = Vector3.Length(ThisParticle - ThatParticle);
ThisParticle.Forces.Add(K * X);
}
}
更新機能は、わかりやすいようにバネ力の更新を簡略化しています。
これはいくつかの点で役立ちます。
コードの他の部分に影響を与えることなく、力の計算方法を完全に変更できます。私はいつもこれをしています。同じように、新しい力を追加するのはばかばかしいほど簡単です。IForce インターフェイスを実装している限り、既存のコードとうまく調和することがわかっています。
それが役立つもう1つの方法は、多数の力を処理することです。IForce のリストを持つ強制レジストリがあります。すべてのフォースがそのインターフェースを実装し、更新機能を備えているため、ゲーム内のすべてのフォースを非常に簡単に更新できます。フォースを作成したら、それをリストに追加します。次に、リストをループし、各要素の update 関数を呼び出します。フォースのタイプを気にせずに、すべてのフォースを更新します。
私は毎日、さまざまな状況でインターフェイスを使用しています。彼らは素晴らしいです!
注:インターフェイスは、さまざまなクラスからメソッドやイベントなどを制限してアクセスするために使用されます。これは、任意のクラス内でさらに多くのメソッドを定義できることを意味しますが、インターフェイスを介してメソッドを呼び出す場合、制限されたメソッド以外のみが必要であることを意味します。以下のプログラムでは、User1 は読み取りと書き込みの両方を使用できますが、User2 は書き込みと実行を行うことができます。以下のこのプログラムを参照してください........
namespace ExplConsole
{
class Program
{
static void Main ()
{
System.Console.WriteLine("Permission for User1");
User1 usr1 = new Test(); // Create instance.
usr1.Read(); // Call method on interface.
usr1.Write();
System.Console.WriteLine("Permission for User2");
User2 usr2 = new Test();
usr2.Write();
usr2.Execute();
System.Console.ReadKey();
}
}
interface User1
{
void Read();
void Write();
}
interface User2
{
void Write();
void Execute();
}
class Test : NewTest,User1, User2
{
public void Read()
{
Console.WriteLine("Read");
}
public void Write()
{
Console.WriteLine("Write");
}
}
class NewTest
{
public void Execute()
{
Console.WriteLine("Execute");
}
}
}
出力:
Permission for User1
Read
Write
Permission for User2
Write
Execute
インターフェイスは、動作ではなく、オブジェクトのパブリック要素 (プロパティ、メソッド、イベントなど)の契約を定義するだけです。
interface IDog
{
void WagTail(); //notice no implementation
ISound Speak(); //notice no implementation
}
class Spaniel : IDog
{
public void WagTail()
{
Console.WriteLine("Shook my long, hairy tail");
}
public ISound Speak()
{
return new BarkSound("yip");
}
}
class Terrier : IDog
{
public void WagTail()
{
Console.WriteLine("Shook my short tail");
}
public ISound Speak()
{
return new BarkSound("woof");
}
}
アップデート
「実際の例」では、次のインターフェイスを使用します。 - ユニット テスト - GENERICS (例: リポジトリ、ゲートウェイ、設定)
interface Repository<T>{
T Find(Predicate<T>);
List<T> ListAll();
}
interface Gateway<T>{
T GetFrom(IQuery query);
void AddToDatabase(IEntity entityItem);
}
interface Settings<T>{
string Name { get; set; }
T Value { get; set; }
T Default { get; }
}
私の意見では、簡単な答えは、インターフェイスに少し慣れていないことですが、クラスにインターフェイスを実装することは、本質的に「このクラスはインターフェイスで関数(およびパラメーター)を定義する必要がある」ことを意味します。
それから、特定のクラスがインターフェースを実装するときはいつでも、それらの関数を呼び出すことができることを確認できます。
そうでなければ異なる複数のクラスが同じインターフェイスを実装する場合、それらすべてをインターフェイスに「キャスト」し、それらのすべてのインターフェイス関数を呼び出すことができます。これは、各クラスが関数の異なる実装を持つ可能性があるため、異なる効果を持つ可能性があります。
たとえば、ユーザーが 4 種類のマップを生成できるプログラムを作成しています。そのために、4 種類のジェネレーター クラスを作成しました。ただし、それらはすべて「IGenerator」インターフェースを実装しています。
public interface IGenerator {
public void generateNow(int period);
}
これは、少なくとも「public generateNow(int period)」関数を定義するように指示します。
私が最初に持っていたジェネレーターが何であれ、それを「IGenerator」にキャストした後、「generateNow(4)」を呼び出すことができます。返されたジェネレーターのタイプを確認する必要はありません。つまり、巨大な if ステートメントで「クラス 1 の変数インスタンス」、「クラス 2 の変数インスタンス」などはもう必要ありません。
多重継承とは、複数の状況でクラスを使用できるようにすることです: [疑似コード]
interface Shape {
// shape methods like draw, move, getboundingrect, whatever.
}
interface Serializable {
// methods like read and write
}
class Circle : public Shape, public Serializable {
// TODO: implement Shape methods
// TODO: implement Serializable methods
}
// somewhere later
{
Circle circle;
// ...
deserializer.deserialize(circle);
// ...
graphicsurface.draw(circle);
// ...
serializer.serialize(circle);
}
Circle クラスは、非常に異なる状況で使用される 2 つの異なるインターフェイスを実装するという考え方です。
これが1つです(Javaですが、類似しているため重要ではありません):私のプロジェクトでは、単純なインターフェースを作成しました:
public interface Identifiable<T> {
public T getId();
}
これは、ある種の注釈の単純な置き換えです。次のステップ: すべてのエンティティ クラスにこのインターフェイスを実装させました。
3 番目のステップは、いくつかの構文糖のようなメソッドを記述することです。
public <T> List<T> ids(List<? extends Identifiable<T> entities) { ... }
これはほんの一例です。
より複雑な例は、検証ルールのようなものです。いくつかの検証エンジン (おそらくあなたが作成したもの) と、ルール用の単純なインターフェイスがあります。
public interface ValidationRule {
public boolean isValid(...);
}
したがって、このエンジンでは、ユーザーがルールを実装する必要があります。そしてもちろん、単一のルールよりも多くのルールが必要になるため、複数の継承があります。
抽象的すぎると邪魔になることがあり、実装の詳細を参照すると実際に物事が明確になります。したがって、最終的にそれらを理解するようになったインターフェイスの金属の説明に近いものを提供します。
インターフェイスは、クラスがいくつかの仮想関数を実装し、これらの仮想関数をクラスのvtableに配置する方法を宣言する方法にすぎません。インターフェイスを宣言するときは、基本的に、仮想関数テーブルの高レベルの説明をコンパイラに提供します。インターフェイスを実装すると、そのインターフェイスによって参照される vtable をクラスに含めることをコンパイラに伝えます。
インターフェイスの目的は、インターフェイス I を実装するクラスをインターフェイス I のインスタンスに暗黙的にキャストできるようにすることです。
interface I {
void doStuff();
}
class Foo : I {
void doStuff() {}
void useAnI(I i) {}
}
var foo = new Foo();
I i = foo; // i is now a reference to the vtable pointer for I in foo.
foo.useAnI(i); // Works. You've passed useAnI a Foo, which can be used as an I.
よく知っているもの、つまり C# の List コレクションを見てみましょう。リストは IList インターフェイスを定義し、ジェネリック リストは IList インターフェイスを定義します。IList は Add、Remove などの関数を公開し、List はこれらの関数を実装します。少し異なる方法で IList を実装する BindingLists もあります。
Head First Design Patternsもお勧めします。コード例は Java ですが、C# に簡単に変換できます。また、インターフェイスとデザイン パターンの真の力を紹介します。