Google mengajukan pertanyaan serupa dengan jawaban yang menurut saya sangat bagus. Saya kutip di bawah ini.
Ada perbedaan lain yang mengintai di sini yang dijelaskan dalam esai Cook yang saya tautkan.
Objek bukan satu-satunya cara untuk mengimplementasikan abstraksi. Tidak semuanya adalah objek. Objek mengimplementasikan sesuatu yang oleh sebagian orang disebut abstraksi data prosedural. Tipe data abstrak menerapkan bentuk abstraksi yang berbeda.
Perbedaan utama muncul ketika Anda mempertimbangkan metode / fungsi biner. Dengan abstraksi data prosedural (objek), Anda dapat menulis sesuatu seperti ini untuk antarmuka set Int:
interface IntSet {
void unionWith(IntSet s);
...
}
Sekarang pertimbangkan dua implementasi IntSet, katakanlah yang didukung oleh daftar dan yang didukung oleh struktur pohon biner yang lebih efisien:
class ListIntSet implements IntSet {
void unionWith(IntSet s){ ... }
}
class BSTIntSet implements IntSet {
void unionWith(IntSet s){ ... }
}
Perhatikan bahwa unionWith harus mengambil argumen IntSet. Bukan tipe yang lebih spesifik seperti ListIntSet atau BSTIntSet. Ini berarti bahwa implementasi BSTIntSet tidak dapat mengasumsikan bahwa inputnya adalah BSTIntSet dan menggunakan fakta tersebut untuk memberikan implementasi yang efisien. (Itu bisa menggunakan beberapa informasi tipe waktu berjalan untuk memeriksanya dan menggunakan algoritma yang lebih efisien jika ya, tetapi masih bisa dilewatkan ListIntSet dan harus kembali ke algoritma yang kurang efisien).
Bandingkan ini dengan ADT, tempat Anda dapat menulis sesuatu yang lebih seperti yang berikut ini di file tanda tangan atau header:
typedef struct IntSetStruct *IntSetType;
void union(IntSetType s1, IntSetType s2);
Kami memprogram melawan antarmuka ini. Khususnya, tipe ini dibiarkan abstrak. Anda tidak tahu apa itu. Kemudian kami memiliki implementasi BST kemudian memberikan jenis konkret dan operasi:
struct IntSetStruct {
int value;
struct IntSetStruct* left;
struct IntSetStruct* right;
}
void union(IntSetType s1, IntSetType s2){ ... }
Sekarang union sebenarnya mengetahui representasi konkrit dari kedua s1 dan s2, sehingga dapat memanfaatkan ini untuk implementasi yang efisien. Kami juga dapat menulis implementasi yang didukung daftar dan memilih untuk menautkannya.
Saya telah menulis sintaks C (ish), tetapi Anda harus melihat misalnya Standar ML untuk tipe data abstrak yang dilakukan dengan benar (di mana Anda dapat misalnya menggunakan lebih dari satu implementasi ADT dalam program yang sama secara kasar dengan mengklasifikasikan jenis-jenis: BSTImpl. IntSetStruct dan ListImpl.IntSetStruct, katakanlah)
Kebalikan dari ini adalah bahwa abstraksi data prosedural (objek) memungkinkan Anda untuk dengan mudah memperkenalkan implementasi baru yang bekerja dengan yang lama. mis. Anda dapat menulis implementasi LoggingIntSet kustom Anda sendiri, dan menyatukannya dengan BSTIntSet. Tapi ini adalah kompromi: Anda kehilangan tipe informatif untuk metode biner! Seringkali Anda akhirnya harus mengekspos lebih banyak fungsionalitas dan detail implementasi di antarmuka Anda daripada yang Anda lakukan dengan implementasi ADT. Sekarang saya merasa seperti saya mengetik ulang esai Cook, jadi sungguh, bacalah!
Saya ingin menambahkan contoh untuk ini.
Cook menyarankan bahwa contoh tipe data abstrak adalah modul dalam C. Memang, modul dalam C melibatkan penyembunyian informasi, karena ada fungsi publik yang diekspor melalui file header, dan fungsi statis (pribadi) yang tidak. Selain itu, sering ada konstruktor (mis. List_new ()) dan pengamat (mis. List_getListHead ()).
Poin kunci dari apa yang membuat, misalnya, modul daftar bernama LIST_MODULE_SINGLY_LINKED ADT adalah bahwa fungsi-fungsi modul (mis. List_getListHead ()) mengasumsikan bahwa input data yang dibuat telah dibuat oleh konstruktor LIST_MODULE_SINGLY_LINKED, sebagai kebalikan dari apa pun "setara" "implementasi daftar (mis. LIST_MODULE_DYNAMIC_ARRAY). Ini berarti bahwa fungsi LIST_MODULE_SINGLY_LINKED dapat mengasumsikan, dalam implementasinya, representasi tertentu (misalnya daftar yang ditautkan sendiri).
LIST_MODULE_SINGLY_LINKED tidak dapat berinteraksi dengan LIST_MODULE_DYNAMIC_ARRAY karena kami tidak dapat memberi makan data yang dibuat, katakanlah dengan konstruktor LIST_MODULE_DYNAMIC_ARRAY, kepada pengamat LIST_MODULE_SINGLY_LINKED karena menganggap LIST_ menganggap sebuah asumsi sebagai sebuah perilaku yang dianggap sebagai suatu asumsi, karena menganggap LIST_ sebagai asumsi untuk menganggap sebuah asumsi sebagai sebuah asumsi (hanya anggapan sebagai suatu anggapan) untuk menganggap sebuah asumsi sebagai suatu anggapan yang dianggap sebagai suatu anggapan).
Ini analog dengan cara dua kelompok berbeda dari aljabar abstrak tidak dapat beroperasi (yaitu, Anda tidak dapat mengambil produk dari elemen dari satu grup dengan elemen dari grup lain). Ini karena grup mengasumsikan properti penutupan grup (produk elemen dalam grup harus dalam grup). Namun, jika kita dapat membuktikan bahwa dua grup berbeda sebenarnya adalah subkelompok dari grup G lain, maka kita dapat menggunakan produk G untuk menambahkan dua elemen, satu dari masing-masing dua grup.
Membandingkan ADT dan objek
Cook mengikat perbedaan antara ADT dan objek sebagian dengan masalah ekspresi. Secara kasar, ADT digabungkan dengan fungsi generik yang sering diimplementasikan dalam bahasa pemrograman fungsional, sementara objek digabungkan dengan Java "objek" yang diakses melalui antarmuka. Untuk keperluan teks ini, fungsi generik adalah fungsi yang mengambil argumen ARGS dan tipe TYPE (pra-kondisi); berdasarkan TYPE itu memilih fungsi yang sesuai, dan mengevaluasinya dengan ARGS (pasca-kondisi). Baik fungsi generik dan objek menerapkan polimorfisme, tetapi dengan fungsi generik, programmer TAHU fungsi yang akan dieksekusi oleh fungsi generik tanpa melihat kode fungsi generik. Dengan objek di sisi lain, programmer tidak tahu bagaimana objek akan menangani argumen, kecuali jika programmer melihat kode objek.
Biasanya masalah ekspresi dipikirkan dalam istilah "apakah saya punya banyak representasi?" vs. "apakah saya memiliki banyak fungsi dengan sedikit representasi". Dalam kasus pertama seseorang harus mengatur kode dengan representasi (seperti yang paling umum, terutama di Jawa). Dalam kasus kedua seseorang harus mengatur kode berdasarkan fungsi (yaitu memiliki fungsi generik tunggal yang menangani banyak representasi).
Jika Anda mengatur kode Anda dengan representasi, maka, jika Anda ingin menambahkan fungsionalitas tambahan, Anda dipaksa untuk menambahkan fungsionalitas ke setiap representasi objek; dalam hal ini menambahkan fungsionalitas bukanlah "aditif". Jika Anda mengatur kode Anda berdasarkan fungsionalitas, maka, jika Anda ingin menambahkan representasi tambahan - Anda dipaksa untuk menambahkan representasi ke setiap objek; dalam hal ini menambahkan representasi dalam bukan "aditif".
Keuntungan ADT dibanding objek
Menambahkan fungsionalitas adalah tambahan
Dimungkinkan untuk meningkatkan pengetahuan tentang representasi ADT untuk kinerja, atau untuk membuktikan bahwa ADT akan menjamin beberapa postcondition diberikan prasyarat. Ini berarti bahwa pemrograman dengan ADT adalah tentang melakukan hal-hal yang benar dalam urutan yang benar (merangkai bersama prasyarat dan prasyarat menuju kondisi pos "tujuan").
Keuntungan objek lebih dari ADT
Menambahkan representasi dalam aditif
Objek dapat saling beroperasi
Dimungkinkan untuk menentukan kondisi sebelum / sesudah suatu objek, dan rantai ini bersama-sama seperti halnya dengan ADT. Dalam hal ini, kelebihan objek adalah (1) mudah untuk mengubah representasi tanpa mengubah antarmuka dan (2) objek dapat saling beroperasi. Namun, ini mengalahkan tujuan OOP dalam arti smalltalk. (lihat bagian "Versi Alan Kay dari OOP)
Pengiriman dinamis adalah kunci untuk OOP
Seharusnya jelas sekarang bahwa pengiriman dinamis (yaitu keterlambatan mengikat) sangat penting untuk pemrograman berorientasi objek. Ini dimaksudkan untuk mendefinisikan prosedur dengan cara yang umum, yang tidak mengasumsikan representasi tertentu. Untuk menjadi konkret - pemrograman berorientasi objek mudah dalam python, karena dimungkinkan untuk memprogram metode objek dengan cara yang tidak menganggap representasi tertentu. Inilah sebabnya mengapa python tidak membutuhkan antarmuka seperti Java.
Di Jawa, kelas adalah ADT. Namun, kelas yang diakses melalui antarmuka yang diimplementasikannya adalah objek.
Tambahan: versi Alan Kay dari OOP
Alan Kay secara eksplisit menyebut objek sebagai "keluarga aljabar", dan Cook menyarankan bahwa ADT adalah aljabar. Karenanya Kay kemungkinan berarti bahwa objek adalah keluarga ADT. Artinya, objek adalah kumpulan semua kelas yang memenuhi antarmuka Java.
Namun, gambar objek yang dilukis oleh Cook jauh lebih ketat daripada visi Alan Kay. Dia ingin benda berperilaku sebagai komputer dalam jaringan, atau sebagai sel biologis. Idenya adalah untuk menerapkan prinsip komitmen paling rendah untuk pemrograman - sehingga mudah untuk mengubah lapisan tingkat rendah dari ADT begitu lapisan tingkat tinggi telah dibangun menggunakannya. Dengan mengingat gambar ini, antarmuka Java terlalu ketat karena mereka tidak mengizinkan objek untuk menafsirkan makna pesan , atau bahkan mengabaikannya sama sekali.
Singkatnya, ide kunci objek, untuk Kay - bukanlah bahwa mereka adalah keluarga aljabar (seperti yang ditekankan oleh Cook). Sebaliknya, ide kunci Kay adalah menerapkan model yang bekerja di besar (komputer dalam jaringan) ke kecil (objek dalam suatu program).
sunting: Klarifikasi lain pada versi Kay OOP: Tujuan objek adalah untuk bergerak lebih dekat ke ideal deklaratif. Kita harus memberi tahu objek apa yang harus dilakukan - tidak memberi tahu bagaimana dengan pengelolaan mikro adalah keadaan, seperti kebiasaan dengan pemrograman prosedural dan ADT. Info lebih lanjut dapat ditemukan di sini , di sini , di sini , dan di sini .
sunting: Saya menemukan eksposisi yang sangat, sangat bagus dari definisi Alan Kay tentang OOP di sini .