Memahami “pemrograman ke suatu antarmuka”

29

Saya telah menemukan banyak istilah "pemrograman ke antarmuka bukan implementasi", dan saya pikir saya agak mengerti apa artinya. Tetapi saya ingin memastikan bahwa saya memahami manfaatnya dan kemungkinan implementasinya.

"Pemrograman ke antarmuka" berarti, jika memungkinkan, seseorang harus merujuk ke tingkat kelas yang lebih abstrak (antarmuka, kelas abstrak, atau terkadang superclass), daripada merujuk pada implementasi konkret.

Contoh umum di Jawa, adalah menggunakan:

List myList = new ArrayList();bukannya ArrayList myList = new ArrayList();.

Saya punya dua pertanyaan tentang ini:

  1. Saya ingin memastikan bahwa saya memahami manfaat utama dari pendekatan ini. Saya pikir manfaatnya kebanyakan fleksibilitas. Mendeklarasikan objek sebagai referensi tingkat lebih tinggi, daripada implementasi konkret, memungkinkan lebih banyak fleksibilitas dan pemeliharaan sepanjang siklus pengembangan dan seluruh kode. Apakah ini benar? Apakah fleksibilitas merupakan manfaat utama?

  2. Apakah ada lebih banyak cara 'pemrograman ke antarmuka'? Atau "mendeklarasikan variabel sebagai antarmuka daripada implementasi konkret" satu-satunya implementasi konsep ini?

Saya tidak berbicara tentang Java construct Interface . Saya berbicara tentang prinsip OO "pemrograman ke antarmuka, bukan implementasi". Dalam prinsip ini, "antarmuka" dunia mengacu pada "supertipe" dari suatu kelas - antarmuka, kelas abstrak, atau superclass sederhana yang lebih abstrak dan lebih konkret daripada subkelas yang lebih konkret.

Aviv Cohn
sumber
kemungkinan duplikat Mengapa antarmuka bermanfaat?
nyamuk
1
Kemungkinan duplikat dari Mengapa instantiate objek ke kelas Base daripada sub kelas tertentu?
Tulains Córdova
1
Jawaban ini memberi Anda contoh yang mudah dipahami programmers.stackexchange.com/a/314084/61852
Tulains Córdova

Jawaban:

44

"Pemrograman ke antarmuka" berarti, jika memungkinkan, seseorang harus merujuk ke tingkat kelas yang lebih abstrak (antarmuka, kelas abstrak, atau terkadang superclass), daripada merujuk pada implementasi konkret.

Ini tidak benar . Atau setidaknya, itu tidak sepenuhnya benar.

Poin yang lebih penting datang dari perspektif desain program. Di sini, "pemrograman ke antarmuka" berarti memfokuskan desain Anda pada apa yang dilakukan kode, bukan bagaimana melakukannya. Ini adalah perbedaan penting yang mendorong desain Anda ke arah kebenaran dan fleksibilitas.

Gagasan utamanya adalah perubahan domain jauh lebih lambat daripada perangkat lunak. Katakanlah Anda memiliki perangkat lunak untuk melacak daftar belanjaan Anda. Di tahun 80-an, perangkat lunak ini akan bekerja melawan baris perintah dan beberapa file datar pada floppy disk. Maka Anda mendapat UI. Maka Anda mungkin meletakkan daftar di database. Kemudian mungkin pindah ke cloud atau ponsel atau integrasi facebook.

Jika Anda merancang kode secara khusus di sekitar implementasi (floppy disk dan baris perintah), Anda tidak akan siap untuk perubahan. Jika Anda mendesain kode di sekitar antarmuka (memanipulasi daftar belanjaan) maka implementasinya bebas untuk diubah.

Telastyn
sumber
Terima kasih atas jawabannya. Menilai dari apa yang Anda tulis, saya pikir saya mengerti apa arti "pemrograman ke antarmuka" dan apa manfaatnya. Tapi saya punya satu pertanyaan - Contoh konkret yang paling umum untuk konsep ini adalah ini: Ketika membuat referensi ke suatu objek, buat tipe referensi tipe antarmuka yang diterapkan objek ini (atau superclass yang diwarisi objek ini), alih-alih membuat tipe referensi tipe objek. (Aka, List myList = new ArrayList()bukannya ArrayList myList = new ArrayList(). (Pertanyaan ada di komentar berikutnya)
Aviv Cohn
Pertanyaan saya adalah: Dapatkah Anda memberi saya lebih banyak contoh untuk tempat-tempat dalam kode dunia nyata di mana prinsip "pemrograman ke antarmuka" terjadi? Selain contoh umum yang saya jelaskan di komentar terakhir?
Aviv Cohn
6
TIDAK . List/ ArrayListTidak apa yang saya bicarakan sama sekali. Ini lebih seperti menyediakan Employeeobjek daripada set tabel tertaut yang Anda gunakan untuk menyimpan catatan Karyawan. Atau menyediakan antarmuka untuk beralih melalui lagu, dan tidak peduli jika lagu itu diacak, atau di CD, atau streaming dari internet. Itu hanya urutan lagu.
Telastyn
Apakah Anda akan mengatakan bahwa "pemrograman ke antarmuka, bukan ke implementasi" adalah prinsip yang mengekspresikan prinsip Abstraksi OO?
Aviv Cohn
1
@ AvivCohn Saya akan menyarankan SpringFramework sebagai contoh yang baik. Semua yang ada di Spring dapat ditingkatkan atau dikustomisasi dengan membuat implementasi antarmuka Anda sendiri dan fitur utama Springs akan tetap berfungsi seperti yang diharapkan ... Atau jika terjadi kustomisasi, seperti yang Anda harapkan. Pemrograman ke antarmuka juga merupakan strategi terbaik untuk merancang kerangka integrasi . Sekali lagi Spring melakukannya dengan integrasi pegasnya. Pada waktu desain, pemrograman ke antarmuka adalah apa yang kita lakukan dengan UML. Atau itu yang akan saya lakukan. Abstraksi sendiri dari caranya dan fokus pada apa yang harus dilakukan.
Laiv
18

Pemahaman saya tentang "pemrograman ke antarmuka" berbeda dari apa yang disarankan pertanyaan atau jawaban lainnya. Yang tidak mengatakan bahwa pemahaman saya benar, atau bahwa hal-hal dalam jawaban lain bukanlah ide yang baik, hanya saja mereka tidak seperti yang saya pikirkan ketika saya mendengar istilah itu.

Pemrograman ke antarmuka berarti bahwa ketika Anda disajikan dengan beberapa antarmuka pemrograman (baik itu perpustakaan kelas, satu set fungsi, protokol jaringan atau apa pun) Anda tetap menggunakan hanya hal-hal yang dijamin oleh antarmuka. Anda mungkin memiliki pengetahuan tentang implementasi yang mendasarinya (Anda mungkin telah menulisnya), tetapi Anda tidak boleh menggunakan pengetahuan itu.

Sebagai contoh, katakanlah API memberi Anda beberapa nilai buram yang merupakan "pegangan" untuk sesuatu yang internal. Pengetahuan Anda mungkin memberi tahu Anda bahwa pegangan ini benar-benar sebuah penunjuk, dan Anda dapat men-dereferensi dan mengakses beberapa nilai, yang memungkinkan Anda untuk dengan mudah menyelesaikan beberapa tugas yang ingin Anda lakukan. Tetapi antarmuka tidak memberi Anda opsi itu; itu pengetahuan Anda tentang implementasi tertentu yang dilakukan.

Masalah dengan ini adalah bahwa ia menciptakan hubungan yang kuat antara kode Anda dan implementasi, persis apa yang seharusnya mencegah antarmuka. Bergantung pada politik, itu bisa berarti bahwa implementasi tidak lagi dapat diubah, karena itu akan merusak kode Anda, atau bahwa kode Anda sangat rapuh dan terus melanggar setiap peningkatan atau perubahan implementasi yang mendasarinya.

Contoh besar dari ini adalah program yang ditulis untuk Windows. WinAPI adalah sebuah antarmuka, tetapi banyak orang menggunakan trik yang berfungsi karena implementasi khusus di, katakanlah Windows 95. Trik ini mungkin membuat program mereka lebih cepat, atau memungkinkan mereka untuk melakukan hal-hal dalam kode kurang dari yang seharusnya diperlukan. Tetapi trik ini juga berarti bahwa program akan macet di Windows 2000, karena API diterapkan secara berbeda di sana. Jika program itu cukup penting, Microsoft mungkin benar-benar maju dan menambahkan beberapa hack untuk implementasi mereka sehingga program akan terus bekerja, tetapi biaya yang meningkat kompleksitas (dengan semua masalah yang terjadi) dari kode Windows. Ini juga membuat hidup menjadi sangat sulit bagi orang-orang Wine, karena mereka mencoba mengimplementasikan WinAPI juga, tetapi mereka hanya dapat merujuk pada dokumentasi untuk bagaimana melakukan ini,

Sebastian Redl
sumber
Itu poin yang bagus, dan saya sering mendengar ini dalam konteks tertentu.
Telastyn
Saya mengerti maksud Anda. Jadi biarkan saya melihat apakah saya dapat menyesuaikan apa yang Anda katakan dengan pemrograman umum: Katakanlah saya memiliki kelas (kelas A) yang menggunakan fungsionalitas kelas abstrak B. Kelas C dan D mewarisi kelas B - mereka menyediakan implementasi konkret untuk apa yang kelas dikatakan dilakukan. Jika kelas A menggunakan langsung kelas C atau D, itu disebut 'pemrograman untuk implementasi', yang bukan solusi yang sangat fleksibel. Tetapi jika kelas A menggunakan referensi ke kelas B, yang nantinya dapat diatur ke implementasi C atau implementasi D, itu membuat hal-hal lebih fleksibel dan dapat dipertahankan. Apakah ini benar?
Aviv Cohn
Jika ini benar, maka pertanyaan saya adalah - Apakah ada contoh yang lebih konkret untuk 'pemrograman ke antarmuka', selain dari 'menggunakan referensi antarmuka bukan contoh umum referensi beton'?
Aviv Cohn
2
@AvivCohn Sedikit terlambat dalam balasan ini, tetapi satu contoh nyata adalah world wide web. Selama perang browser (era IE 4) situs web ditulis tidak sesuai dengan spesifikasi apa yang dikatakan, tetapi untuk kebiasaan beberapa browser (Netscape atau IE). Ini pada dasarnya pemrograman untuk implementasi, bukan antarmuka.
Sebastian Redl
9

Saya hanya dapat berbicara tentang pengalaman pribadi saya, karena ini juga tidak pernah diajarkan secara formal kepada saya.

Poin pertama Anda benar. Fleksibilitas yang diperoleh berasal dari ketidakmampuan untuk secara tidak sengaja meminta detail implementasi dari kelas konkret di mana mereka tidak boleh diminta.

Misalnya, pertimbangkan ILoggerantarmuka yang saat ini diimplementasikan sebagai LogToEmailLoggerkelas konkret . The LogToEmailLoggerkelas mengekspos semua ILoggermetode dan properti, tetapi juga terjadi untuk memiliki properti implementasi khusus sysAdminEmail.

Ketika logger Anda digunakan dalam aplikasi Anda, itu seharusnya tidak menjadi perhatian dari kode memakan untuk mengatur sysAdminEmail. Properti ini harus ditetapkan selama pengaturan logger dan harus disembunyikan dari dunia.

Jika Anda mengkode terhadap implementasi konkret, maka Anda mungkin secara tidak sengaja mengatur properti implementasi saat menggunakan logger. Sekarang, kode aplikasi Anda secara erat digabungkan ke logger Anda, dan beralih ke logger lain akan memerlukan decoupling kode Anda dari yang asli.

Dalam pengertian ini, pengkodean ke antarmuka melonggarkan kopling .

Mengenai poin kedua Anda: Alasan lain yang saya lihat untuk pengkodean ke antarmuka adalah untuk mengurangi kompleksitas kode.

Sebagai contoh, bayangkan saya memiliki permainan dengan antarmuka berikut I2DRenderable, I3DRenderable, IUpdateable. Tidak jarang satu komponen game memiliki konten 2D dan 3D yang dapat diurai. Komponen lain mungkin hanya 2D dan lainnya hanya 3D.

Jika rendering 2D dilakukan oleh satu modul, maka masuk akal untuk mempertahankan koleksi I2DRenderables. Tidak masalah jika objek dalam koleksinya juga I3DRenderableatau IUpdateblesebagai modul lain akan bertanggung jawab untuk merawat aspek-aspek objek.

Menyimpan objek yang dapat I2DRenderabledi render sebagai daftar menjaga kompleksitas rendering class tetap rendah. 3D Rendering dan Update logic tidak menjadi perhatiannya sehingga aspek-aspek objek anak-anaknya dapat dan harus diabaikan.

Dalam hal ini, pengkodean ke antarmuka menjaga kompleksitas tetap rendah dengan mengisolasi kekhawatiran .

MetaFight
sumber
4

Mungkin ada dua penggunaan kata antarmuka yang digunakan di sini. Antarmuka yang Anda maksudkan terutama dalam pertanyaan Anda adalah Java Interface . Itu secara khusus konsep Java, lebih umum itu adalah antarmuka bahasa pemrograman.

Saya akan mengatakan bahwa pemrograman ke antarmuka adalah konsep yang lebih luas. API REST yang sekarang populer yang tersedia untuk banyak situs web adalah contoh lain dari konsep pemrograman yang lebih luas untuk antarmuka di tingkat yang lebih tinggi. Dengan membuat lapisan di antara cara kerja kode Anda dan dunia luar (orang-orang di internet, program lain, bahkan bagian lain dari program yang sama) Anda dapat mengubah apa pun di dalam kode Anda selama Anda tidak mengubah apa yang dunia luar mengharapkan, di mana itu ditentukan oleh antarmuka, atau kontrak yang ingin Anda hormati.

Itu kemudian memberikan Anda fleksibilitas untuk memperbaiki kode internal Anda tanpa harus mengatakan semua hal lain yang bergantung pada antarmuka.

Ini juga berarti bahwa kode Anda harus lebih stabil. Dengan tetap berpegang pada antarmuka maka Anda tidak boleh merusak kode orang lain. Ketika Anda benar-benar harus mengubah antarmuka maka Anda dapat merilis versi utama baru (1.abc ke 2.xyz) dari API yang menandakan bahwa ada perubahan yang melanggar pada antarmuka versi baru.

Seperti @Doval tunjukkan dalam komentar pada jawaban ini ada juga kesalahan lokal. Ini saya pikir semua bermuara pada enkapsulasi. Sama seperti Anda akan menggunakannya untuk objek dalam desain Berorientasi Objek, jadi konsep ini juga berguna di tingkat yang lebih tinggi.

Encaitar
sumber
1
Manfaat yang biasanya diabaikan adalah kesalahan lokal. Katakanlah Anda membutuhkan Peta, dan Anda menerapkannya menggunakan pohon biner. Agar ini berfungsi, kunci harus memiliki beberapa urutan, dan Anda harus menjaga yang lain bahwa kunci yang "kurang dari" kunci simpul saat ini ada di subtree kiri, sedangkan yang "lebih besar dari" ada di subtree yang tepat. Saat Anda menyembunyikan implementasi Peta di belakang sebuah antarmuka, jika pencarian Peta salah, Anda tahu bug harus ada dalam modul Peta. Jika terpapar, bug bisa berada di mana saja dalam program. Bagi saya, inilah manfaat utama.
Doval
4

Analogi Dunia Nyata mungkin membantu:

Colokan listrik listrik di Antarmuka.
Iya nih; benda tiga pin di ujung kabel daya dari TV, radio, penyedot debu, mesin cuci, dll.

Setiap alat yang memiliki steker utama (yaitu mengimplementasikan antarmuka "memiliki steker listrik") dapat diperlakukan dengan cara yang persis sama; mereka semua dapat dicolokkan ke stopkontak di dinding dan dapat menarik daya dari soket itu.

Apa setiap alat individu tidak sama sekali berbeda. Anda tidak akan terlalu jauh membersihkan karpet dengan TV dan kebanyakan orang tidak menonton mesin cuci mereka untuk hiburan. Tetapi semua peralatan ini memiliki perilaku yang sama yaitu dapat dicolokkan ke stopkontak dinding.

Itulah yang membuat Anda mendapatkan Antarmuka. Perilaku
terpadu yang dapat dilakukan oleh banyak Kelas Obyek berbeda, tanpa perlu komplikasi Warisan.

Phill W.
sumber
1

Istilah "pemrograman ke antarmuka" terbuka untuk banyak interpretasi. Antarmuka dalam pengembangan perangkat lunak adalah kata yang sangat umum. Inilah cara saya menjelaskan konsep ini kepada pengembang junior yang telah saya latih selama bertahun-tahun.

Dalam arsitektur perangkat lunak ada berbagai batasan alami. Contoh umum termasuk

  • Batas jaringan antara proses klien dan server
  • Batas API antara aplikasi dan perpustakaan pihak ketiga
  • Batas kode internal antara berbagai domain bisnis dalam program

Yang penting, adalah bahwa ketika batas-batas alami ini ada, mereka diidentifikasi dan kontrak tentang bagaimana batas itu berperilaku ditentukan. Anda menguji perangkat lunak Anda bukan dengan apakah "pihak lain" berperilaku, tetapi apakah interaksi Anda sesuai dengan spesifikasi.

Konsekuensi dari ini adalah:

  • Komponen eksternal dapat dipertukarkan selama mereka menerapkan spesifikasi
  • Titik alami untuk pengujian unit untuk memvalidasi perilaku yang benar
  • Tes integrasi menjadi penting - apakah spesifikasinya ambigu?
  • Sebagai pengembang, Anda memiliki dunia yang lebih kecil ketika mengerjakan tugas tertentu

Sementara banyak dari ini dapat berhubungan dengan kelas dan antarmuka, penting untuk menyadari bahwa itu juga berkaitan dengan model data, protokol jaringan dan dalam cara yang lebih umum bekerja dengan banyak pengembang

Michael Shaw
sumber