グーグルは私が非常に良いと思う答えで同様の質問を持ち出しました。以下に引用しました。
ここに潜んでいる別の区別があり、それは私がリンクしたクックエッセイで説明されています。
オブジェクトは、抽象化を実装する唯一の方法ではありません。すべてがオブジェクトではありません。オブジェクトは、手続き型データ抽象化と呼ばれるものを実装しています。抽象データ型は、異なる形式の抽象化を実装します。
バイナリメソッド/関数を検討すると、重要な違いが現れます。手続き型データの抽象化(オブジェクト)を使用すると、Intセットインターフェイスに対して次のような記述を行うことができます。
interface IntSet {
void unionWith(IntSet s);
...
}
次に、IntSetの2つの実装について考えてみましょう。1つはリストによってサポートされ、もう1つはより効率的なバイナリツリー構造によってサポートされます。
class ListIntSet implements IntSet {
void unionWith(IntSet s){ ... }
}
class BSTIntSet implements IntSet {
void unionWith(IntSet s){ ... }
}
unionWithはIntSet引数を取る必要があることに注意してください。ListIntSetやBSTIntSetのようなより具体的なタイプではありません。これは、BSTIntSet実装は、その入力がBSTIntSetであると想定できず、その事実を使用して効率的な実装を提供できないことを意味します。(それはそれをチェックするためにいくつかの実行時タイプ情報を使用し、それがより効率的なアルゴリズムを使用する可能性がありますが、それでもListIntSetが渡され、より効率の悪いアルゴリズムにフォールバックする必要があります)。
これをADTと比較してください。シグネチャファイルまたはヘッダーファイルに次のようなものを記述できます。
typedef struct IntSetStruct *IntSetType;
void union(IntSetType s1, IntSetType s2);
このインターフェイスに対してプログラミングします。特に、型は抽象のままです。あなたはそれが何であるかを知ることはありません。次に、BST実装があり、具体的な型と操作を提供します。
struct IntSetStruct {
int value;
struct IntSetStruct* left;
struct IntSetStruct* right;
}
void union(IntSetType s1, IntSetType s2){ ... }
これでunionは実際にs1とs2の両方の具体的な表現を知っているので、これを利用して効率的な実装を行うことができます。リストに裏付けされた実装を記述して、代わりにそれとリンクすることを選択することもできます。
私はC(ish)構文を書きましたが、たとえば、適切に行われた抽象データ型の標準MLを確認する必要があります(たとえば、型を修飾することによって、同じプログラムで実際にADTの複数の実装を実際に使用できます:BSTImpl。 IntSetStructおよびListImpl.IntSetStructなど)
これとは逆に、手続き型のデータ抽象化(オブジェクト)を使用すると、古い実装で機能する新しい実装を簡単に導入できます。たとえば、独自のカスタムLoggingIntSet実装を作成し、それをBSTIntSetと結合できます。しかし、これはトレードオフです。バイナリメソッドの有益な型を失うことになります。多くの場合、ADT実装よりも多くの機能と実装の詳細をインターフェイスに公開する必要があります。クックのエッセイを書き直しているような気がするので、実際に読んでください!
これに例を追加したいと思います。
クックは、抽象データ型の例はCのモジュールであることを示唆しています。実際、Cのモジュールには情報の非表示が含まれます。これは、ヘッダーファイルを通じてエクスポートされるパブリック関数と、そうでない静的(プライベート)関数があるためです。さらに、多くの場合、コンストラクター(list_new()など)とオブザーバー(list_getListHead()など)があります。
たとえば、LIST_MODULE_SINGLY_LINKEDと呼ばれるリストモジュールをADTにする重要な点は、モジュールの関数(たとえば、list_getListHead())は、入力されるデータがLIST_MODULE_SINGLY_LINKEDのコンストラクターによって作成されたものであり、 "リストの実装(例:LIST_MODULE_DYNAMIC_ARRAY)。これは、LIST_MODULE_SINGLY_LINKEDの関数が、それらの実装において、特定の表現(たとえば、単一リンクリスト)を想定できることを意味します。
LIST_MODULE_SINGLY_LINKEDはオブジェクトの表現であると見なすため、LIST_MODULE_SINGLY_LINKEDはオブジェクトと見なされるため、LIST_MODULE_SINGLY_LINKEDはオブジェクトと見なされるため、LIST_MODULE_SINGLY_LINKEDはオブジェクトと見なされます。
これは、抽象代数の2つの異なるグループが相互運用できない方法に似ています(つまり、あるグループの要素と別のグループの要素の積をとることはできません)。これは、グループがグループのクロージャプロパティを想定しているためです(グループ内の要素の積はグループ内にある必要があります)。ただし、2つの異なるグループが実際には別のグループGのサブグループであることを証明できる場合、Gの積を使用して、2つのグループのそれぞれから1つずつ、2つの要素を追加できます。
ADTとオブジェクトの比較
Cookは、ADTとオブジェクトの違いを部分的に表現の問題に結び付けます。大まかに言えば、ADTは関数型プログラミング言語で実装されることが多い汎用関数と結合され、オブジェクトはインターフェースを介してアクセスされるJava「オブジェクト」と結合されます。このテキストでは、ジェネリック関数は、いくつかの引数ARGSと型TYPE(前提条件)を取る関数です。TYPEに基づいて適切な関数を選択し、それをARGS(事後条件)で評価します。ジェネリック関数とオブジェクトはどちらもポリモーフィズムを実装しますが、プログラマーはジェネリック関数のコードを調べずに、ジェネリック関数によってどの関数が実行されるかを知っています。一方、オブジェクトの場合、プログラマーは、オブジェクトのコードを調べない限り、オブジェクトが引数を処理する方法を知りません。
通常、表現の問題は「多くの表現があるか」という観点から考えられます。対「表現が少ない関数がたくさんありますか」。最初のケースでは、コードを表現別に編成する必要があります(特にJavaで最も一般的です)。2番目のケースでは、関数ごとにコードを編成する必要があります(つまり、単一の汎用関数で複数の表現を処理します)。
あなたが表現してコードを整理する場合は、余分な機能を追加したい場合は、その後、あなたがされて強制的にオブジェクトのすべての表現に機能を追加します。この意味で、機能の追加は「追加」ではありません。あなたが機能してコードを整理する場合は、余分な表現を追加したい場合は、その後、 -あなたがしている強制的にすべてのオブジェクトに表現を追加します。この意味で、表現の追加は「付加的」ではありません。
オブジェクトに対するADTの利点
ADTに対するオブジェクトの利点
動的ディスパッチはOOPの鍵です
オブジェクト指向プログラミングには動的ディスパッチ(つまり、遅延バインディング)が不可欠であることは明らかです。これは、特定の表現を前提としない一般的な方法でプロシージャを定義できるようにするためです。具体的に言うと、特定の表現を前提としない方法でオブジェクトのメソッドをプログラミングできるため、Pythonではオブジェクト指向プログラミングが簡単です。これが、PythonがJavaのようなインターフェースを必要としない理由です。
Javaでは、クラスはADTです。ただし、実装するインターフェースを介してアクセスされるクラスはオブジェクトです。
補遺:アラン・ケイのOOPバージョン
Alan Kayはオブジェクトを明示的に「代数の族」と呼び、CookはADTが代数であることを示唆しています。したがって、ケイはおそらくオブジェクトがADTのファミリーであることを意味していました。つまり、オブジェクトは、Javaインターフェースを満たすすべてのクラスのコレクションです。
ただし、クックによって描かれたオブジェクトの写真は、アランケイのビジョンよりもはるかに制限されています。彼は、オブジェクトがネットワーク内のコンピューターとして、または生体細胞として動作することを望んでいました。アイデアは、プログラミングへの最小のコミットメントの原則を適用することでした。これにより、ADTの低レベルレイヤーを使用して、高レベルレイヤーがいったん構築されると、簡単に変更できます。この図を念頭に置いておくと、オブジェクトがメッセージの意味を解釈したり、メッセージを完全に無視したりできないため、Javaインターフェースは制限が厳しすぎます。
要約すると、ケイにとってのオブジェクトの重要なアイデアは、オブジェクトが代数のファミリーであるということではありません(クックによって強調されています)。むしろ、Kayの重要なアイデアは、大規模(ネットワーク内のコンピューター)で機能するモデルを小規模(プログラム内のオブジェクト)に適用することでした。
編集:ケイのバージョンのOOPに関するもう1つの明確化:オブジェクトの目的は、宣言的な理想に近づくことです。手続き型プログラミングやADTで慣例となっているように、オブジェクトを何をすべきかを伝える必要があります- マイクロ管理によって状態をどのように伝えるかではなく、詳細情報を見つけることができ、ここで、ここでは、ここでは、とここに。
編集:私はここで、Alan KayによるOOPの定義の非常に良い説明を見つけました。