インターフェースがメンバーよりもメソッドを必要とするのはなぜですか?


8

...これにより、ゲッターとセッターを作成する必要が生じますが、実際にはまったく無関係です。ほとんど(すべて?)の言語のインターフェイスで、メンバーフィールドがインターフェイス仕様を満たすことを許可しないのは、言語設計上の理由がありますか?

ほとんどの現存する言語では、メンバーはメソッドとは根本的に異なる方法で扱われますが、これは当てはまりますか?理論的な言語では、(a)引数なしのメソッドをゲッターとして指定し、読み取り可能なメンバーをそのインプリメンターとして受け入れ、(b)単一引数のメソッドをセッターとして指定したが受け入れられたインターフェイスはありませんでしたか?変数の宣言時に変数への直接アクセス制御の指定をサポートする言語が与えられた場合、その実装者としての書き込み可能なメンバーフィールド 複数の継承を許可するプロトタイプ言語(一部の回答者が作成した非常に有効なポイントに対処するため)は、このようなものに最適です。

このようなものがすでに存在する場合はお知らせください。


2
ええと...メソッドメンバーです。「メンバーフィールド」という意味ですか?
アーロンノート、2011

適切なJava用語を使用してもかまいません。「メンバーフィールド」です。ご指摘いただきありがとうございます。
エンジニア、

1
OOPはひどいですが、必要であればC#を試してください。
ジョブ

回答:


3

明らかに、ほとんどの現存する言語では、メンバーはメソッドとは根本的に異なる方法で扱われますが、これは当てはまりますか?

理論的に根拠のある理由ではありません。あなたがそれを頻繁に見ない理由は実用的だと思います:

1つ目は、実行時にこれらのことをどのように処理するかです。JavaまたはC ++では、プリミティブ型のパブリックメンバーへの割り当てfoo.x = 5は簡単に処理できます。たとえば、あるレジスタにデータ5を格納し、メモリのどこに住んxfooいるかを調べ、そこにレジスタをダンプします。クラスがメンバーへのインターセプト割り当てをカプセル化したり、それらのメンバーが評価する方法を事前に計算したりできる C#のような言語では、コンパイラーにはアプリオリがありません。実行時に利用可能にされたクラスの実装がその機能を使用するかどうかの知識。つまり、すべてのパブリックメンバーの割り当てとパブリックメンバーの評価は実装に委ねる必要があります。*これらのメンバーへの多くのアクセスを伴う操作は、定期的に高速に処理できるサブルーチン呼び出しを常に実行しているため、パフォーマンスに影響を与えますインライン割り当て。評価と割り当てをゲッターやセッターとは別にすることで、開発者は特定の状況でそれらをどのように処理するかをある程度制御できます。

第二は年齢です。F#の例外を除いて、今日非常に広く使用されている言語は10年未満であり、その当時、インターセプトされた評価と割り当てを可能にするために必要な追加のサイクルは受け入れられなかった可能性があります。

第三は慣性です。クラスがパブリックメンバーを操作することを言語が許可していても、人々はまだゲッターとセッターのほうが優れていると考えていても、ある程度、パブリックメンバーへの直接割り当てに関連する不名誉はまだあると思います。それは、アセンブリの代わりに高水準言語でプログラムを書いた人々よりも空中で鼻を持っているすべての人々と同じように、時間とともにすり減っていくでしょう。


*これに伴うトレードオフがあります:特定のクラスのインライン評価/割り当てを使用するコードがそれらをインターセプトする実装とリンクしようとする場合、言語はクラスへの評価/割り当てを延期するか、実行時に停止する必要があります。個人的には、後者は変更されていないAPIを使用するコードの再コンパイルを強制するため、悪い考えだと思います。


ありがとうございます。あなたは私の質問の主旨を理解し、WHYのザラザラした詳細を理解しました。速度が理由の1つであるという予感がありました。「よりダイナミックな」道を進むときはいつもそうです。JSやPythonなどの言語で現在見られるように、柔軟性は速度よりも優先されることが多いため、(かつて必要だった)最適化に基づく特定の言語設計の傾向に言及した「慣性」はゆっくりと解消されます。
エンジニア、

F#は、Objective CAMLに大きく基づいています。10年未満の可能性がありますが、ほとんど(おそらくすべて)のアイデアは古いものです。
Steve314、2011

1
2人の反対投票者が赤いボタンを押した理由がちょっと気になります。
Blrfl 2011

@Blrfl:私の批判のポイントは、あなたの答えは言語設計の観点から見るのではなく、実装の詳細に焦点を合わせすぎているということでしょう。私はそれのためにあなたに反対票を投じなかったが、おそらく他の人はそうした。
Niels van der Rest、

Scalaも比較的新しく、広く使用されています
表示名

10

ほとんどの現存する言語では、メンバー[フィールド?]はメソッドとは根本的に異なる方法で処理されますが、これは当てはまりますか?

はい、そうです。

メンバーフィールドはインスタンスごとのデータです。データは実際には、インスタンスごとにメモリ内のどこかに存在している必要があります。データ用に予約されたアドレスまたは一連のアドレスが必要です。Java、.NET、および他の多くのオブジェクト指向言語では、このデータはヒープに蓄積されます。

メソッドはクラスごとのデータです。これらはvtableへのポインタです。これが、メソッドが仮想ディスパッチを取得する方法です。クラスごとに実際に割り当てられるインスタンスは1つだけです。

インターフェイスは基本的に、インスタンスデータではなくメソッドのみを含む特別なタイプのクラスです。それはインターフェースの定義の一部です。データが含まれている場合は、インターフェイスではなくなり、抽象クラスになります。もちろん、実装しようとしているデザインパターンの種類によっては、どちらを選択しても問題ありません。しかし、インスタンスデータをインターフェイスに追加した場合それらをインターフェイスにするすべてのものを取り除くことになります。

では、インターフェイスメソッドがメンバーフィールドをポイントできないのはなぜですか?指摘するべきクラスレベルには何もないからです。この場合も、インターフェイスはメソッドテーブルにすぎません。コンパイラは1つのテーブルしか生成できず、その中のすべてのメソッドは同じメモリアドレスを指す必要があります。したがって、クラスフィールドはインスタンスごとに異なるアドレスであるため、インターフェイスメソッドがクラスフィールドをポイントすることは不可能です。

おそらく、コンパイラーはこれを受け入れて、インターフェースが指すゲッターメソッド生成できますが、それが本質的にプロパティになります。Javaにプロパティがない理由を知りたいのであれば、これは本当に長くて退屈な話なので、ここでは説明しません。もし興味があるなら、あなたは他のオブジェクト指向言語で見ていたいかもしれません、そのようなC#やデルファイなどのプロパティを実装し、。構文は本当に簡単です:

public interface IFoo
{
    int Bar { get; }
}

public class Foo : IFoo
{
    public int Bar { get; set; }
}

うん、それだけです。重要:これらはフィールドではなくFooクラスの「int Bar」はAuto Propertyであり、コンパイラーは上記で説明したとおり正確に実行しています。ゲッターとセッター(およびバッキングメンバーフィールド)を自動的に生成します。

だからあなたの質問への答えを要約するには:

  • インターフェースの目的はデータを含まないことなので、インターフェースにはメソッドが必要です。
  • クラスがインターフェースを実装しやすくする構文上の砂糖がないのは、Javaデザイナーがインターフェースを実装しないという単純な理由によるものです。現時点では、下位互換性が問題です。言語デザイナーはこのような選択をします。あなたはそれに対処するか、切り替える必要があります。

これは良い答えです。特に、関数ルックアップに関する情報が参考になりました。ありがとう。
エンジニア

9

これは情報の非表示の結果です。つまり、これらのメンバーはフィールドとして表示されるべきではなく、ローカルに格納/キャッシュされるか、またはキャッシュされないプロパティとして表示されます。

1つの例は、オブジェクトが変更されたときに変更されるハッシュコードです。オブジェクトが変更されるたびではなく、要求されたときに計算する方が適切です。

さらに、ほとんどの言語は単一継承+複数実装であり、フィールドをインターフェイスの一部にすることで、複数継承の領域に入ることができます(欠落しているのは、関数のデフォルト実装のみです)。


ここではマルチ実装フィールドが問題になります。
DeadMG

これについてさらに考えてみれば、多重継承についてはあまり同意できません。私が実際に求めているのは、データメンバーを使用してインターフェイス(基本的にはC ++抽象クラス)を構築できるかどうかではなく、インターフェイスでのデータメンバーの表現が可能かどうかです。そこには大きな違いがあります。
エンジニア

MIにはまったく問題ありません。言語は、同じメンバーに対して競合する型を持つ2つのインターフェースを実装することを急ぐ必要があります。
Blrfl 2011

6

メンバー変数は、インターフェイスではなくクラスの実装です。実装を変更したい場合があるため、他のクラスがその実装を直接参照することを許可しないでください。

次のメソッドを持つクラスを検討してください-C ++構文ですが、それは重要ではありません...

class example
{
  public:
    virtual void Set_Mode (int p_mode);
    virtual int  Mode () const;
};

メンバーと呼ばれる変数があるように起こっていることはかなり明白なようだmodeか、m_mode直接店舗モード値は、「プロパティ」は、いくつかの言語でどうなるのか、かなりやっていることというか、似た-しかし、それは必ずしも真実ではありません。これを処理する方法はたくさんあります。たとえば、Set_Modeメソッドは...

  1. switchステートメントを使用してモードを識別します。
  2. そのモード値に応じて、いくつかの内部クラスのサブクラスをインスタンス化します。
  3. そのインスタンスへのポインタをメンバー変数として格納します。

これを行うと、例のクラスの他の多くのメソッドが、ポインターを介してその内部クラスの適切なメソッドを呼び出し、現在のモードを確認する必要なくモード固有の動作を取得できます。

ここでのポイントは、この種のことを実装するための可能な方法が複数存在するということではなく、いつかは気が変わるかもしれません。単純な変数から始めたのかもしれませんが、すべてのメソッドのモードを特定するすべてのスイッチステートメントに問題が生じています。あるいは、インスタンスポインターのことから始めたのに、状況にとって重すぎることが判明したのかもしれません。

このようなことがすべてのメンバーデータで発生する可能性があることがわかりました。値が格納される単位をマイルからキロメートルに変更する必要がある場合があります。または、一意に識別されるケースの列挙では、追加情報などを考慮せずにすべてのケースを一意に識別できない場合があります。

これはメソッドでも発生する可能性があります。一部のメソッドは純粋に内部で使用されるものであり、実装によって異なり、プライベートである必要があります。しかし、多くのメソッドはインターフェースの一部であり、クラスの内部実装が置き換えられたからといって、名前を変更する必要はありません。

とにかく、実装が公開されている場合、他のコードは必然的にそれに依存し始め、その実装を維持することにロックされます。実装が他のコードから隠されている場合、そのような状況は起こり得ません。

実装の詳細へのアクセスをブロックすることを「データ非表示」と呼びます。


「実装ではなくインターフェースへのコード」...そうです。時には私たちはこれらのことを潜在的に知っていますが、あなたがしたようにそれらを推論して、もう一度概念を本当に固めるには誰かが必要になります。
エンジニア

OK-ちょっと待って。さらに考えた後、質問に対する私の編集を参照してください。
エンジニア

編集内容を把握するのは少し難しいです。何らかの理由で、古い編集の一部の差分が表示されません。しかし、私があなたの言っていることは、「プロパティがデフォルトの実装としてメンバー変数を持つことができないのはなぜですか?」です。プロパティはメンバー変数のように見えます(インターフェイス)が、getter / setterメソッドを介して実装されます。そして、IMOありませんなし、デフォルトでは、それらのgetter / setterメソッドは、メンバ変数への読み取り/書き込みを実装してはならない理由は-またはそれは安全なとき、なぜゲッターとセッターの呼び出しが離れて最適化されるべきではない-継承を可能にし、別の編集など
Steve314

@Nick-そうです、「このプロパティのゲッターおよび/またはセッターは純粋でなければならない」または「デフォルトのものではない」と言える限り、それは良いアイデアだと思います-プロパティの一般的なケース。すでにそれを実行する言語があるかどうかはわかりません-プロパティが存在する言語でもプロパティをあまり使用していません-これがまだサポートされていない場合は驚きます。
Steve314、2011

3

他の人が述べたように、インターフェースはクラスが何をするではなく、何をするかを記述します。これは基本的に、継承クラスが準拠しなければならないコントラクトです。

あなたが説明するものは少し異なります- 再利用の形式として、いくつかではなくおそらくすべてではない実装を提供するクラス。これらは一般に抽象クラスと呼ばれ、多くの言語(C ++やJavaなど)でサポートされています。これらのクラスは、それ自体でインスタンス化することはできません。具体的な(またはさらに抽象的な)クラスによってのみ継承できます。

すでに述べたように、抽象クラスは、より大きなクラスに追加機能の一部を提供するために使用できるため、多重継承をサポートする言語で最も価値がある傾向があります。ただし、これは一般的に悪いスタイルと見なされますが、一般的には集約( "has-a")の関係の方が適切です。


いい答えです。私は最近、私が引き受けている種類の仕事のほとんど全体で作文を支持する傾向があります。このボード上の他の人が述べているように、私は特定の言語での抽象クラスとインターフェイスの個別の扱いを個人的には信じていませんラチェットフリークの答えに)。
エンジニア

1
私はあなたのコメントre:abstract / interfacesに同意する傾向があります。多くの抽象クラスを使用した古いコードを振り返ると、10〜15年以上の経験から、言語機能を使用して
不適切

1

このようなものがすでに存在する場合はお知らせください。

Objective-Cプロパティは、合成されたプロパティアクセサーでこの種の機能を提供します。Objective-Cのプロパティは、基本的に、クラスがアクセサー(通常はfooゲッターとsetFoo:セッターの形式)を提供するという約束にすぎません。プロパティ宣言は、メモリ管理とスレッド動作に関連するアクセサの特定の属性を指定します。@synthesizeコンパイラーにアクセサーと対応するインスタンス変数を生成するように指示するディレクティブもあります。そのため、ゲッターとセッターを作成したり、各プロパティのivarを宣言したりする必要はありません。プロパティへのアクセスを構造体のフィールドにアクセスするように見せかける構文上の砂糖もありますが、実際にはプロパティアクセサーが呼び出されます。

これらすべての結果、プロパティを持つクラスを宣言できます。

@interface MyClass : NSObject
{}
@property (assign) int foo;
@property (copy) NSString *bar;
@end

その後、直接ivarにアクセスするように見えるコードを記述できますが、実際にはアクセサーメソッドを使用します。

MyClass *myObject = [[MyClass alloc] init];  // create myObject

// setters
myObject.foo = 127;                 // same as [myObject setFoo:127];
myObject.bar = @"Hello, world!";    // same as [myObject setBar:@"Hello, world!"];

// getters
int i = myObject.foo                // same as [myObject foo];
NSString *s = myObject.bar          // same as [myObject bar];

アクセサーを使用すると、インターフェイスを実装から分離できると既に言われていますが、これを行う理由は、クライアントコードに影響を与えずに将来実装を変更できるようにするためです。欠点は、将来のオプションを開いたままにするためだけに、アクセサを作成して使用するのに十分な規律を持つ必要があることです。Objective-Cは、ivarを直接使用するのと同じくらい簡単に、アクセサーの生成と使用を可能にします。実際、メモリ管理の作業の多くを処理するため、プロパティを使用する方がivarに直接アクセスするよりもはるかに簡単です。また、実装を変更したいときは、コンパイラーに自動的に生成させる代わりに、いつでも独自のアクセサーを提供できます。


1

インターフェースがメンバーよりもメソッドを必要とするのはなぜですか?

...これにより、ゲッターとセッターを作成する必要が生じますが、実際にはまったく無関係です。ほとんどの(すべての)言語のインターフェイスで、メンバーがインターフェイスの仕様を満たすことを許可しない、言語設計上の理由はありますか?

「ほとんどの」言語?どの言語を調査しましたか?動的言語にはインターフェースはありません。静的に型付けされたオブジェクト指向言語は、Java、C#、Objective-C、C ++の3つまたは4つしかありません。C ++にはインターフェースがありません。代わりに、抽象クラスを使用します。抽象クラスには、パブリックデータメンバーを含めることができます。C#とObjective-Cはどちらもプロパティをサポートしているため、これらの言語でゲッターやセッターを使用する必要はありません。それはJavaだけを残します。おそらく、Javaがプロパティをサポートしない唯一の理由は、下位互換性のある方法でプロパティを導入することが困難であったためです。


メンバーは、単に「メソッド」と呼んでいる「メンバー関数」ではなく、「メンバーフィールド」を指すことを意味していました。この慣習を採用した言語(ある場合)がわかりません。だから私が書いたことを考えると、私はあなたの答えを見ることができますが、私の質問の下のコメントは、より建設的だったと明確にするために推測します。
エンジニア

C ++に触れます。ただし、実際には動的言語のインターフェースがあり、JSとPythonの2つがすぐに思い浮かびます。静的に型付けされた言語の支持者がこれらの「インターフェース」を呼び出すかどうかは、まったく別の議論であり、理解する必要はありません。個人的には、さまざまな目的でさまざまな言語を使用しているので、そのフェンスのどちら側にもほとんど好みを示していません。
エンジニア

@Nick:JSとPythonには継承がありますが、インターフェースがあるとは言いません。任意のオブジェクトの任意のメソッドを呼び出すことができます。
kevin cline、2011

1

一部の回答では、インターフェースメソッドがフィールドよりも好まれる理由として、「情報の非表示」と「実装の詳細」について言及しています。主な理由はそれよりも単純だと思います。

インターフェイスは、複数のクラスにインターフェイスを実装させることで動作を交換できる言語機能です。クラスの動作は、そのメソッドによって定義されます。一方、フィールドはオブジェクトの状態のみを反映します。

もちろん、クラスのフィールドまたはプロパティは動作の一部である可能性ありますが、それらはインターフェースによって定義されたコントラクトの重要な部分ではありません。それらは単なる結果です。そのため、たとえばC#ではインターフェイスでプロパティを定義でき、Javaではインターフェイスでゲッターを定義できます。ここで、情報の隠蔽が少し出てきますが、それが主なポイントではありません。

前に述べたように、インターフェースを使用すると動作をスワップアウトできますが、スワップアウト部分を見てみましょう。インターフェイスを使用すると、新しい実装クラスを作成することにより、基礎となる実装変更できます。したがって、フィールドの実装について多くを変更することができないため、フィールドで構成されるインターフェースを持つことはあまり意味がありません。

フィールドのみのインターフェースを作成する唯一の理由は、あるクラス特定のタイプのオブジェクトであると言いたいときです。インターフェースではなく継承を使用する必要があるため、「is a」の部分を強調しました。


補足:継承よりも優先される構成と構成は通常、インターフェースを使用することによってのみ達成できることを知っていますが、いくつかのプロパティを持つクラスに「タグを付ける」ためにインターフェースを使用することも同様に悪いです。継承は適切に使用された場合に優れており、単一責任の原則を守っていれば問題はありません。

つまり、継承を使用しないUIコントロールライブラリなどを使用したことがありません。ここでインターフェイスが機能する唯一の領域は、データプロビジョニング、たとえばITableDataAdapterデータをテーブルコントロールにバインドすることです。これがされ、正確なものインタフェースのタイプが持っている、のために意図されている交換可能な行動を


0

Javaでの理由は、インターフェースはメソッドの指定のみを許可するためです。

したがって、インターフェイスコントラクトのフィールドを持つものが必要な場合は、それをゲッターおよびセッターメソッドとして実行する必要があります。

この質問も参照してください:ゲッターとセッターはいつ正当化されますか


1
返信に来てくれてありがとう、私はこの質問をするようになったのは、まさにそのトピックに関するあなたの返答でした:)しかし、質問は残ります、「-なぜ-彼らはメソッドしか許可しなかったのですか?」インターフェイスのポイントは、メソッドを指定することではなく、循環ロジックです。インターフェイスの要点は、ある程度の基本的な機能が確実に満たされるようにすることです。メンバーはその機能の一部を構成できます。それでは、なぜ彼らがインターフェースの履行に参加することを許可しないのですか?
エンジニア、

私は知らない。ゴスリングやスティールに直接聞いたとしたら?

-1

インターフェイスの要点は、このインターフェイスをサポートすると主張するために実装する必要がある関数を指定することです。


2
これは質問の答えにはなりません。問題は、なぜ彼らはそれをそのようにしたのですか?すべての変数のゲッターとセッターを作成せずに、まだインターフェイスを実現できると考えたことはありますか?時間を節約できます!
エンジニア、

あなたはゲッターとセッターについて尋ね続けます、そして私はなぜか分かりません。シンプルなインターフェイスを見て:download.oracle.com/javase/6/docs/api/java/util/... お知らせどのように何のゲッターとセッターはありませんか?
Scott C Wilson

1
それは何を証明しますか?その1つの例は、メンバーへの単純/直接アクセスが必要ないことを証明していますか?いいえ、要点は、言語自体がそれを自動的に処理できる場合、インターフェイスの一部としてゲッター/セッターにラップメンバーを含めるのはばかげたことです。
エンジニア、

このような例は他にも何百もあります。あなたが話している状況の例を教えてください。
Scott C Wilson

-1

これらの言語は恐ろしい悪、つまり多重継承に対処する必要があるため、メンバーはインターフェイスで許可されていません。


2
わかりません。インターフェースでメンバー変数を許可すると、多重継承のサポートが必要または暗黙的になるのはなぜですか?
スティーブマルラム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.