Apakah saya perlu menggunakan antarmuka saat hanya satu kelas yang akan mengimplementasikannya?

243

Bukankah seluruh titik dari antarmuka yang banyak kelas mematuhi seperangkat aturan dan implementasi?

Lamin Sanneh
sumber
16
Atau untuk membuatnya lebih mudah unittest.
11
Mengizinkan beberapa kelas untuk mengimplementasikan Antarmuka dan membuat kode Anda bergantung pada antarmuka sangat penting untuk isolasi untuk pengujian unit. Jika Anda melakukan pengujian unit, Anda akan memiliki kelas lain yang mengimplementasikan antarmuka itu.
StuperUser
2
Diskusi reddit tentang pertanyaan ini.
yannis
6
Bidang dan metode publik adalah "antarmuka" sendiri. Jika kurangnya polimorfisme sengaja direncanakan maka tidak ada alasan untuk menggunakan antarmuka. Unit yang menguji orang lain disebutkan adalah rencana penggunaan polimorfisme.
mike30

Jawaban:

205

Sebenarnya, tidak, YAGNI berlaku. Yang mengatakan, waktu yang Anda habiskan untuk membuat antarmuka sangat minim, terutama jika Anda memiliki alat pembuat kode praktis melakukan sebagian besar pekerjaan untuk Anda. Jika Anda tidak yakin apakah Anda akan memerlukan antarmuka atau tidak, saya akan mengatakan lebih baik untuk berbuat salah dalam mendukung definisi antarmuka.

Selain itu, menggunakan antarmuka bahkan untuk satu kelas akan memberi Anda implementasi tiruan lain untuk unit test, yang tidak diproduksi. Jawaban Avner Shahar-Kashtan diperluas pada titik ini.

yannis
sumber
84
Pengujian +1 berarti Anda hampir selalu memiliki dua implementasi
jk.
24
@YannisRizos Tidak setuju dengan poin Anda yang terakhir karena Yagni. Cranking antarmuka dari metode publik kelas sepele setelah fakta, seperti mengganti CFoo dengan IFoo dalam mengkonsumsi kelas. Tidak ada gunanya menuliskannya sebelum kebutuhan.
Dan Neely
5
Saya masih tidak yakin saya mengikuti alasan Anda. Karena alat pembuat kode membuat menambahkannya setelah faktanya bahkan lebih murah, saya melihat lebih sedikit alasan untuk membuat antarmuka sebelum Anda memiliki kebutuhan eksplisit untuk itu.
Dan Neely
6
Saya pikir Interface yang hilang bukan contoh yang baik untuk YAGNI tetapi untuk "broken Window" dan Dokumentasi yang hilang. Pengguna kelas praktis dipaksa untuk kode terhadap implementasi, bukan abstraksi sebagaimana mestinya.
Fabio Fracassi
10
Mengapa Anda pernah mencemari basis kode Anda dengan cruft tidak berarti hanya untuk memenuhi beberapa kerangka pengujian? Maksud saya serius, saya hanya seorang pria sisi klien JavaScript otodidak mencoba untuk memilah WTF salah dengan C # dan implementasi Java pengembang OOD saya terus menghadapi dalam upaya saya untuk menjadi lebih dari seorang generalis yang berpengetahuan luas tetapi mengapa tidak t mereka menampar IDE dari tangan Anda dan tidak membiarkan Anda memilikinya kembali sampai Anda belajar cara menulis kode yang bersih dan dapat dibaca ketika mereka mengetahui Anda melakukan hal semacam itu di perguruan tinggi? Itu hanya cabul.
Erik Reppen
144

Saya akan menjawab bahwa apakah Anda memerlukan antarmuka atau tidak tidak tergantung pada berapa banyak kelas yang akan mengimplementasikannya. Antarmuka adalah alat untuk mendefinisikan kontrak antara beberapa subsistem aplikasi Anda; jadi yang terpenting adalah bagaimana aplikasi Anda dibagi menjadi beberapa subsistem. Seharusnya ada antarmuka sebagai front-end untuk subsistem yang dienkapsulasi, tidak peduli berapa banyak kelas yang mengimplementasikannya.

Inilah salah satu aturan praktis yang sangat berguna:

  • Jika Anda membuat kelas Foomerujuk langsung ke kelas BarImpl, Anda sangat berkomitmen untuk berubah Foosetiap kali Anda berubah BarImpl. Anda pada dasarnya memperlakukan mereka sebagai satu unit kode yang dibagi menjadi dua kelas.
  • Jika Anda Foomerujuk ke antarmuka Bar , Anda malah mengkomit diri sendiri untuk menghindari perubahan Foosaat Anda berubah BarImpl.

Jika Anda mendefinisikan antarmuka pada titik-titik kunci aplikasi Anda, Anda memikirkan metode yang harus mereka dukung dan mana yang seharusnya tidak didukung, dan Anda mengomentari antarmuka dengan jelas untuk menggambarkan bagaimana suatu implementasi seharusnya berperilaku (dan bagaimana tidak), aplikasi Anda akan jauh lebih mudah untuk dipahami karena antarmuka yang dikomentari ini akan memberikan semacam spesifikasi aplikasi — deskripsi tentang bagaimana ia seharusnya berperilaku. Ini membuatnya lebih mudah untuk membaca kode (daripada bertanya "apa yang seharusnya dilakukan kode ini" Anda bisa bertanya "bagaimana kode ini melakukan apa yang seharusnya dilakukan").

Selain semua ini (atau sebenarnya karena itu), antarmuka mempromosikan kompilasi terpisah. Karena antarmuka sepele untuk dikompilasi dan memiliki lebih sedikit ketergantungan daripada implementasinya, itu berarti bahwa jika Anda menulis kelas Foountuk menggunakan antarmuka Bar, Anda biasanya dapat mengkompilasi ulang BarImpltanpa perlu mengkompilasi ulang Foo. Dalam aplikasi besar ini dapat menghemat banyak waktu.

sakundim
sumber
14
Saya akan memperbaiki ini lebih dari sekali, jika saya bisa. IMO jawaban terbaik untuk pertanyaan ini.
Fabio Fracassi
4
Jika kelas hanya menjalankan satu peran (yaitu hanya akan memiliki satu antarmuka yang ditentukan untuk itu), lalu mengapa tidak melakukan ini melalui metode publik / pribadi?
Matthew Finlay
4
1. Organisasi kode; memiliki antarmuka dalam file sendiri yang hanya memiliki tanda tangan dan komentar dokumentasi membantu menjaga kode tetap bersih. 2. Kerangka kerja yang memaksa Anda untuk mengekspos metode yang Anda lebih suka menjaga pribadi (misalnya, wadah yang menyuntikkan dependensi melalui setter publik). 3. Kompilasi terpisah, seperti yang sudah disebutkan; jika kelas Footergantung pada antarmuka Barmaka BarImpldapat dimodifikasi tanpa harus mengkompilasi ulang Foo. 4. Kontrol akses berbutir halus daripada penawaran publik / pribadi (mengekspos kelas yang sama untuk dua klien melalui antarmuka yang berbeda).
sacundim
3
Dan yang terakhir: 5. Klien (idealnya) tidak peduli berapa banyak kelas yang dimiliki modul saya atau berapa banyak, atau bahkan dapat diceritakan. Yang harus mereka lihat adalah serangkaian jenis , dan beberapa pabrik atau fasad untuk membuat bola bergulir. Yaitu, saya sering melihat nilai dalam merangkum bahkan apa kelas perpustakaan Anda terdiri.
sacundim
4
@sacundim If you make class Foo refer directly to class BarImpl, you're strongly committing yourself to change Foo every time you change BarImplSaat mengubah BarImpl, apa saja perubahan yang dapat dihindari di Foo dengan menggunakan interface Bar? Karena selama tanda tangan & fungsionalitas metode tidak berubah di BarImpl, Foo tidak akan memerlukan perubahan bahkan tanpa antarmuka (seluruh tujuan antarmuka). Saya berbicara tentang skenario di mana hanya satu kelas yaitu BarImplmengimplementasikan Bar. Untuk skenario multi-kelas, saya memahami Prinsip Ketergantungan Pembalikan dan bagaimana antarmuka sangat membantu.
Shishir Gupta
101

Antarmuka ditunjuk untuk mendefinisikan perilaku, yaitu seperangkat prototipe fungsi / metode. Tipe yang mengimplementasikan antarmuka akan mengimplementasikan perilaku itu, jadi ketika Anda berurusan dengan tipe seperti itu Anda tahu (sebagian) perilaku apa yang dimilikinya.

Tidak perlu mendefinisikan antarmuka jika Anda tahu bahwa perilaku yang ditentukan oleh itu akan digunakan hanya sekali. Kiss (tetap simpel, bodoh)

superM
sumber
Tidak selalu, Anda bisa menggunakan antarmuka dalam satu kelas jika kelas itu adalah kelas generik.
John Isaiah Carmona
Selain itu, Anda dapat memperkenalkan antarmuka saat akhirnya dibutuhkan dengan mudah dalam IDE modern dengan refactoring "ekstrak antarmuka".
Hans-Peter Störr
Pertimbangkan kelas utilitas di mana hanya ada metode statis publik dan konstruktor pribadi - dapat diterima dan dipromosikan oleh penulis seperti Joshua Bloch et al.
Darrell Teague
63

Sementara secara teori Anda tidak harus memiliki antarmuka hanya untuk memiliki-an-antarmuka, jawaban Yannis Rizos mengisyaratkan komplikasi lebih lanjut:

Saat Anda menulis tes unit dan menggunakan kerangka kerja tiruan seperti Moq atau FakeItEasy (untuk menyebutkan dua yang paling baru saya gunakan), Anda secara implisit membuat kelas lain yang mengimplementasikan antarmuka. Mencari kode atau analisis statis mungkin mengklaim bahwa hanya ada satu implementasi, tetapi sebenarnya ada implementasi tiruan internal. Setiap kali Anda mulai menulis ejekan, Anda akan menemukan bahwa mengekstraksi antarmuka masuk akal.

Tapi tunggu, masih ada lagi. Ada lebih banyak skenario di mana ada implementasi antarmuka implisit. Menggunakan tumpukan komunikasi .NET WCF, misalnya, menghasilkan proxy ke layanan jarak jauh, yang, sekali lagi, mengimplementasikan antarmuka.

Dalam lingkungan kode bersih, saya setuju dengan sisa jawaban di sini. Namun, perhatikan semua kerangka kerja, pola atau dependensi yang Anda miliki yang mungkin menggunakan antarmuka.

Avner Shahar-Kashtan
sumber
2
+1: Saya tidak yakin YAGNI berlaku di sini, karena memiliki antarmuka dan menggunakan kerangka kerja (misalnya JMock, dll.) Yang menggunakan antarmuka sebenarnya dapat menghemat waktu Anda.
Deco
4
@Deco: kerangka kerja mengejek yang baik (JMock di antara mereka) bahkan tidak perlu antarmuka.
Michael Borgwardt
12
Membuat antarmuka semata-mata karena keterbatasan kerangka mengejek Anda sepertinya merupakan alasan yang mengerikan bagi saya. Misalnya, menggunakan EasyMock mengejek kelas semudah antarmuka.
Alb
8
Saya akan mengatakan sebaliknya. Menggunakan objek tiruan dalam pengujian berarti membuat implementasi alternatif untuk antarmuka Anda, menurut definisi. Ini benar apakah Anda membuat kelas FakeImplementation Anda sendiri atau membiarkan kerangka kerja mengejek melakukan pekerjaan berat untuk Anda. Mungkin ada beberapa kerangka kerja, seperti EasyMock, yang menggunakan berbagai peretasan dan trik tingkat rendah untuk mengejek kelas beton - dan lebih banyak kekuatan untuk mereka! - tetapi secara konseptual, objek tiruan adalah implementasi alternatif untuk kontrak.
Avner Shahar-Kashtan
1
Anda tidak melepaskan tes dalam produksi. Mengapa tiruan untuk kelas yang tidak membutuhkan antarmuka, membutuhkan antarmuka?
Erik Reppen
32

Tidak, Anda tidak membutuhkannya, dan saya menganggapnya sebagai anti-pola untuk secara otomatis membuat antarmuka untuk setiap referensi kelas.

Ada biaya nyata untuk membuat Foo / FooImpl untuk semuanya. IDE dapat membuat antarmuka / implementasi gratis, tetapi ketika Anda menavigasi kode, Anda memiliki beban kognitif tambahan dari F3 / F12 untuk foo.doSomething()membawa Anda ke antarmuka tanda tangan dan bukan implementasi sebenarnya yang Anda inginkan. Plus Anda memiliki kekacauan dua file, bukan satu untuk semuanya.

Jadi Anda hanya harus melakukannya ketika Anda benar-benar membutuhkannya untuk sesuatu.

Sekarang menangani counterarguments:

Saya membutuhkan antarmuka untuk kerangka kerja injeksi Ketergantungan

Antarmuka untuk mendukung kerangka kerja adalah warisan. Di Jawa, antarmuka yang digunakan menjadi persyaratan untuk proxy dinamis, pra-CGLIB. Hari ini, Anda biasanya tidak membutuhkannya. Ini dianggap sebagai kemajuan dan keuntungan bagi produktivitas pengembang yang tidak Anda perlukan lagi di EJB3, Spring dll.

Saya perlu mengejek untuk pengujian unit

Jika Anda menulis tiruan Anda sendiri dan memiliki dua implementasi aktual, maka antarmuka sesuai. Kami mungkin tidak akan melakukan diskusi ini di tempat pertama jika basis kode Anda memiliki FooImpl dan TestFoo.

Tetapi jika Anda menggunakan kerangka mengejek seperti Moq, EasyMock, atau Mockito, Anda bisa mengejek kelas dan Anda tidak perlu antarmuka. Ini analog dengan pengaturan foo.method = mockImplementationdalam bahasa dinamis tempat metode dapat ditetapkan.

Kami membutuhkan antarmuka untuk mengikuti Prinsip Ketergantungan Inversi (DIP)

DIP mengatakan Anda membangun bergantung pada kontrak (antarmuka) dan bukan implementasi. Tapi kelas sudah menjadi kontrak dan abstraksi. Untuk itulah kata kunci publik / pribadi diperuntukkan. Di universitas, contoh kanonik adalah sesuatu seperti kelas Matriks atau Polinomial - konsumen memiliki API publik untuk membuat matriks, menambahkannya, dll. Tetapi tidak diizinkan untuk peduli jika matriks diterapkan dalam bentuk yang jarang atau padat. Tidak ada IMatrix atau MatrixImpl yang diperlukan untuk membuktikan hal itu.

Selain itu, DIP sering diterapkan berlebihan pada setiap tingkat panggilan kelas / metode, tidak hanya pada batas modul utama. Tanda bahwa Anda terlalu menerapkan DIP adalah bahwa antarmuka dan implementasi Anda berubah pada langkah-kunci sehingga Anda harus menyentuh dua file untuk membuat perubahan, bukan satu. Jika DIP diterapkan dengan tepat, itu berarti bahwa antarmuka Anda tidak harus sering berubah. Juga, tanda lain adalah bahwa antarmuka Anda hanya memiliki satu konsumen nyata (aplikasi sendiri). Cerita berbeda jika Anda membangun perpustakaan kelas untuk konsumsi di banyak aplikasi yang berbeda.

Ini adalah akibat wajar dari pendapat Paman Bob Martin tentang mengejek - Anda hanya perlu mengejek batas-batas arsitektur utama. Dalam webapp, akses HTTP dan DB adalah batasan utama. Semua panggilan kelas / metode di antaranya tidak. Hal yang sama berlaku untuk DIP.

Lihat juga:

Wrschneider
sumber
4
Kelas mengejek daripada antarmuka (setidaknya di Jawa dan C #) harus dianggap usang, karena tidak ada cara untuk mencegah konstruktor superclass berjalan, yang dapat menyebabkan objek tiruan Anda berinteraksi dengan lingkungan dengan cara yang tidak disengaja. Mengolok-olok antarmuka lebih aman dan lebih mudah karena Anda tidak perlu memikirkan kode konstruktor.
Jules
4
Saya belum mengalami masalah dengan kelas mengejek, tapi saya merasa frustrasi dengan navigasi IDE yang tidak berfungsi seperti yang diharapkan. Memecahkan masalah nyata mengalahkan masalah hipotetis.
wrschneider
1
@ Jules Anda dapat mengejek kelas beton termasuk konstruktornya di Jawa.
assylias
1
@assylias bagaimana Anda mencegah konstruktor berjalan?
Jules
2
@ Jules Itu tergantung pada kerangka kerja mengejek Anda - dengan jmockit misalnya, Anda bisa menulis new Mockup<YourClass>() {}dan seluruh kelas, termasuk konstruktornya, diejek, apakah itu antarmuka, kelas abstrak atau kelas beton. Anda juga dapat "mengganti" perilaku konstruktor jika Anda ingin melakukannya. Saya kira ada cara yang setara di Mockito atau Powermock.
assylias
22

Sepertinya jawaban di kedua sisi pagar dapat disimpulkan sebagai berikut:

Desain dengan baik, dan letakkan antarmuka di mana antarmuka dibutuhkan.

Seperti yang saya perhatikan dalam jawaban saya untuk jawaban Yanni , saya tidak berpikir Anda bisa memiliki aturan yang keras dan cepat tentang antarmuka. Menurut definisi, aturan tersebut harus fleksibel. Aturan saya tentang antarmuka adalah bahwa antarmuka harus digunakan di mana pun Anda membuat API. Dan API harus dibuat di mana pun Anda melewati batas dari satu domain tanggung jawab ke domain lainnya.

Misalnya (dibuat-buat mengerikan), katakanlah Anda sedang membangun Carkelas. Di kelas Anda, Anda tentu membutuhkan lapisan UI. Dalam contoh khusus ini, dibutuhkan bentuk IginitionSwitch, SteeringWheel, GearShift, GasPedal, dan BrakePedal. Karena mobil ini mengandung AutomaticTransmission, Anda tidak perlu ClutchPedal. (Dan karena ini adalah mobil yang mengerikan, tidak ada a / C, radio, atau kursi. Faktanya, papan lantai juga hilang - Anda hanya perlu berpegang pada setir itu dan berharap yang terbaik!)

Jadi, kelas mana yang membutuhkan antarmuka? Jawabannya bisa semuanya, atau tidak sama sekali - tergantung desain Anda.

Anda dapat memiliki antarmuka yang terlihat seperti ini:

Interface ICabin
    Event IgnitionSwitchTurnedOn()
    Event IgnitionSwitchTurnedOff()
    Event BrakePedalPositionChanged(int percent)
    Event GasPedalPositionChanged(int percent)
    Event GearShiftGearChanged(int gearNum)
    Event SteeringWheelTurned(float degree)
End Interface

Pada saat itu, perilaku kelas-kelas tersebut menjadi bagian dari ICabin Interface / API. Dalam contoh ini kelas-kelas (jika ada beberapa) mungkin sederhana, dengan beberapa properti, dan satu atau dua fungsi. Dan apa yang secara implisit Anda nyatakan dengan desain Anda adalah bahwa kelas-kelas ini ada hanya untuk mendukung implementasi konkret ICabin apa pun yang Anda miliki, dan mereka tidak dapat eksis sendiri, atau mereka tidak ada artinya di luar konteks ICabin.

Itu alasan yang sama bahwa Anda tidak menguji anggota pribadi - mereka hanya ada untuk mendukung API publik, dan dengan demikian perilaku mereka harus diuji dengan menguji API.

Jadi jika kelas Anda ada hanya untuk mendukung kelas lain, dan secara konseptual Anda melihatnya tidak benar - benar memiliki domain sendiri, maka tidak masalah untuk melewati antarmuka. Tetapi jika kelas Anda cukup penting sehingga Anda menganggapnya cukup dewasa untuk memiliki domain sendiri, silakan maju dan berikan antarmuka.


SUNTING:

Sering (termasuk dalam jawaban ini) Anda akan membaca hal-hal seperti 'domain', 'ketergantungan' (sering digabungkan dengan 'injeksi') yang tidak berarti apa-apa bagi Anda ketika Anda mulai memprogram (mereka yakin tidak berarti bagi saya). Untuk domain, artinya persis seperti apa itu:

Wilayah di mana kekuasaan atau kekuasaan diberikan; milik kedaulatan atau persemakmuran, atau sejenisnya. Juga digunakan secara kiasan. [WordNet sense 2] [1913 Webster]

Dalam contoh saya - mari kita pertimbangkan IgnitionSwitch. Di mobil meatspace, kunci kontak bertanggung jawab untuk:

  1. Otentikasi (tidak mengidentifikasi) pengguna (mereka membutuhkan kunci yang benar)
  2. Memberikan arus ke starter sehingga benar-benar dapat menyalakan mobil
  3. Menyediakan arus ke sistem pengapian sehingga dapat terus beroperasi
  4. Matikan arus sehingga mobil akan berhenti.
  5. Bergantung pada bagaimana Anda melihatnya, di sebagian besar (semua?) Mobil baru ada saklar yang mencegah kunci dihapus dari kunci kontak saat transmisi keluar dari Park, jadi ini bisa menjadi bagian dari domainnya. (Sebenarnya itu berarti saya harus memikirkan kembali dan mendesain ulang sistem saya ...)

Properti-properti tersebut membentuk domain dari IgnitionSwitch, atau dengan kata lain, apa yang diketahui dan bertanggung jawab atas hal itu.

Tidak IgnitionSwitchbertanggung jawab atas GasPedal. Sakelar penyalaan sama sekali tidak mengetahui pedal gas dalam segala hal. Mereka berdua beroperasi sepenuhnya independen satu sama lain (meskipun mobil akan cukup berharga tanpa keduanya!).

Seperti yang saya katakan sebelumnya, itu tergantung pada desain Anda. Anda bisa mendesain IgnitionSwitchyang memiliki dua nilai: Aktif ( True) dan Nonaktif ( False). Atau Anda bisa mendesainnya untuk mengotentikasi kunci yang disediakan untuknya, dan sejumlah tindakan lainnya. Itulah bagian sulit dari menjadi seorang pengembang memutuskan di mana harus menarik garis di pasir - dan jujur ​​sebagian besar waktu itu benar-benar relatif. Garis-garis di pasir itu penting - di situlah API Anda berada, dan dengan demikian di mana antarmuka Anda seharusnya.

Wayne Werner
sumber
Bisakah Anda menjelaskan lebih lanjut tentang apa yang Anda maksud dengan "memiliki domain sendiri"?
Lamin Sanneh
1
@LaminSanneh, diuraikan. Apakah itu membantu?
Wayne Werner
8

Tidak (YAGNI) , kecuali jika Anda berencana untuk menulis tes untuk kelas lain menggunakan antarmuka ini, dan tes tersebut akan mendapat manfaat dari mengejek antarmuka.

stijn
sumber
8

Dari MSDN :

Antarmuka lebih cocok untuk situasi di mana aplikasi Anda membutuhkan banyak jenis objek yang mungkin tidak terkait untuk menyediakan fungsionalitas tertentu.

Antarmuka lebih fleksibel daripada kelas dasar karena Anda dapat menentukan implementasi tunggal yang dapat mengimplementasikan banyak antarmuka.

Antarmuka lebih baik dalam situasi di mana Anda tidak perlu mewarisi implementasi dari kelas dasar.

Antarmuka berguna dalam kasus di mana Anda tidak dapat menggunakan warisan kelas. Sebagai contoh, struktur tidak dapat mewarisi dari kelas, tetapi mereka dapat mengimplementasikan antarmuka.

Secara umum dalam kasus kelas tunggal, tidak perlu mengimplementasikan antarmuka, tetapi mempertimbangkan masa depan proyek Anda, mungkin berguna untuk secara formal mendefinisikan perilaku kelas yang diperlukan.

lwm
sumber
5

Untuk menjawab pertanyaan: Ada lebih dari itu.

Salah satu aspek penting dari sebuah antarmuka adalah tujuannya.

Antarmuka adalah "tipe abstrak yang tidak berisi data, tetapi memperlihatkan perilaku" - Antarmuka (komputasi) Jadi jika ini adalah perilaku, atau seperangkat perilaku, yang didukung oleh kelas, dari antarmuka kemungkinan pola yang benar. Namun, jika perilaku tersebut intrinsik dengan konsep yang terkandung oleh kelas, maka Anda mungkin tidak ingin antarmuka sama sekali.

Pertanyaan pertama yang diajukan adalah apa sifat dari hal atau proses yang Anda coba wakili. Kemudian ikuti dengan alasan praktis untuk menerapkan sifat itu dengan cara tertentu.

Joshua Drake
sumber
5

Karena Anda mengajukan pertanyaan ini, saya kira Anda telah melihat minat memiliki antarmuka yang menyembunyikan banyak implementasi. Ini dapat dimanifestasikan oleh prinsip inversi ketergantungan.

Namun, keharusan untuk memiliki antarmuka atau tidak tidak tergantung pada jumlah implementasinya. Peran sebenarnya dari sebuah antarmuka adalah bahwa ia mendefinisikan kontrak yang menyatakan layanan apa yang harus disediakan alih-alih bagaimana seharusnya dilaksanakan.

Setelah kontrak ditetapkan, dua tim atau lebih dapat bekerja secara independen. Katakanlah Anda bekerja pada modul A dan itu tergantung pada modul B, fakta untuk membuat antarmuka pada B memungkinkan untuk melanjutkan pekerjaan Anda tanpa khawatir tentang implementasi B karena semua detail disembunyikan oleh antarmuka. Dengan demikian, pemrograman terdistribusi menjadi mungkin.

Meskipun modul B hanya memiliki satu implementasi antarmuka, antarmuka masih diperlukan.

Sebagai kesimpulan, sebuah antarmuka menyembunyikan detail implementasi dari penggunanya. Pemrograman ke antarmuka membantu untuk menulis lebih banyak dokumen karena kontrak harus ditentukan, untuk menulis lebih banyak perangkat lunak modular, untuk mempromosikan pengujian unit dan untuk mempercepat kecepatan pengembangan.

Hui Wang
sumber
2
Setiap kelas dapat memiliki antarmuka publik (metode publik) dan antarmuka pribadi (detail implementasi). Saya bisa berpendapat bahwa antarmuka publik kelas adalah kontrak. Anda tidak perlu elemen tambahan untuk mengerjakan kontrak itu.
Fuhrmanator
4

Semua jawaban di sini sangat bagus. Memang sebagian besar waktu Anda tidak perlu mengimplementasikan antarmuka yang berbeda. Tetapi ada kasus di mana Anda mungkin ingin tetap melakukannya. Berikut adalah beberapa kasus di mana saya melakukannya:

Kelas mengimplementasikan antarmuka lain yang saya tidak ingin
sering terjadi dengan kelas adaptor yang menjembatani kode pihak ketiga.

interface NameChangeListener { // Implemented by a lot of people
    void nameChanged(String name); 
} 

interface NameChangeCount { // Only implemented by my class
    int getCount();
}

class NameChangeCounter implements NameChangeListener, NameChangeCount {
    ...
}

class SomeUserInterface {
    private NameChangeCount currentCount; // Will never know that you can change the counter
}

Kelas menggunakan teknologi spesifik yang seharusnya tidak bocor
Sebagian besar saat berinteraksi dengan perpustakaan eksternal. Bahkan jika hanya ada satu implementasi, saya menggunakan antarmuka untuk memastikan bahwa saya tidak memperkenalkan penggabungan yang tidak perlu dengan perpustakaan eksternal.

interface SomeRepository { // Guarantee that the external library details won't leak trough
    ...
}

class OracleSomeRepository implements SomeRepository { 
    ... // Oracle prefix allow us to quickly know what is going on in this class
}

Komunikasi lintas lapisan
Bahkan jika hanya satu kelas UI yang pernah mengimplementasikan salah satu kelas domain, itu memungkinkan pemisahan yang lebih baik antara lapisan itu dan yang paling penting itu menghindari ketergantungan siklik.

package project.domain;

interface UserRequestSource {
    public UserRequest getLastRequest();
}

class UserBehaviorAnalyser {
    private UserRequestSource requestSource;
}

package project.ui;

class OrderCompleteDialog extends SomeUIClass implements project.domain.UserRequestSource {
    // UI concern, no need for my domain object to know about this method.
    public void displayLabelInErrorMode(); 

    // They most certainly need to know about *that* though
    public UserRequest getLastRequest();
}

Hanya sebagian dari metode yang harus tersedia untuk sebagian
besar objek. Sebagian besar terjadi ketika saya memiliki beberapa metode konfigurasi pada kelas beton

interface Sender {
    void sendMessage(Message message)
}

class PacketSender implements Sender {
    void sendMessage(Message message);
    void setPacketSize(int sizeInByte);
}

class Throttler { // This class need to have full access to the object
    private PacketSender sender;

    public useLowNetworkUsageMode() {
        sender.setPacketSize(LOW_PACKET_SIZE);
        sender.sendMessage(new NotifyLowNetworkUsageMessage());

        ... // Other details
    }
}

class MailOrder { // Not this one though
    private Sender sender;
}

Jadi pada akhirnya saya menggunakan antarmuka untuk alasan yang sama seperti saya menggunakan bidang pribadi: objek lain seharusnya tidak memiliki akses ke hal-hal yang seharusnya tidak mereka akses. Jika saya memiliki case seperti itu, saya memperkenalkan antarmuka walaupun hanya satu kelas yang mengimplementasikannya.

Laurent Bourgault-Roy
sumber
2

Antarmuka sangat penting tetapi cobalah untuk mengontrol jumlah mereka yang Anda miliki.

Setelah membuat antarmuka untuk hampir semua hal, mudah untuk berakhir dengan kode 'spaghetti cincang'. Saya tunduk pada kebijaksanaan Ayende Rahien yang lebih besar yang telah memposting beberapa kata yang sangat bijak tentang masalah ini:

http://ayende.com/blog/153889/limit-your-abstractions-analyzing-a-ddd-aplikasi

Ini adalah posting pertamanya dari seluruh seri jadi teruslah membaca!

Spa Spa
sumber
Kode 'dicincang spageti' juga dikenal sebagai kode ravioli c2.com/cgi/wiki?RavioliCode
CurtainDog
Kedengarannya lebih seperti lasagna-code atau baklava-code bagi saya - kode dengan terlalu banyak lapisan. ;-)
dodgy_coder
2

Salah satu alasan Anda mungkin masih ingin memperkenalkan antarmuka dalam hal ini adalah untuk mengikuti Prinsip Ketergantungan Inversi . Yaitu, modul yang menggunakan kelas akan bergantung pada abstraksi itu (yaitu antarmuka) bukan tergantung pada implementasi konkret. Ini memisahkan komponen tingkat tinggi dari komponen tingkat rendah.

dodgy_coder
sumber
2

Tidak ada alasan nyata untuk melakukan apa pun. Antarmuka adalah untuk membantu Anda dan bukan program keluaran. Jadi meskipun Antarmuka diimplementasikan oleh sejuta kelas, tidak ada aturan yang mengatakan Anda harus membuatnya. Anda membuatnya sehingga ketika Anda, atau siapa pun yang menggunakan kode Anda, ingin mengubah sesuatu itu meresap ke semua implementasi. Membuat antarmuka akan membantu Anda dalam semua kasus di masa mendatang di mana Anda mungkin ingin membuat kelas lain yang mengimplementasikannya.

John Demetriou
sumber
1

Tidak selalu ada kebutuhan untuk mendefinisikan antarmuka untuk kelas.

Objek sederhana seperti objek nilai tidak memiliki banyak implementasi. Mereka tidak perlu dipermainkan juga. Implementasi dapat diuji sendiri dan ketika kelas lain diuji yang bergantung padanya, objek nilai aktual dapat digunakan.

Ingatlah bahwa membuat antarmuka memiliki biaya. Perlu diperbarui sepanjang implementasi, perlu file tambahan, dan beberapa IDE akan kesulitan memperbesar dalam implementasi, bukan antarmuka.

Jadi saya akan mendefinisikan antarmuka hanya untuk kelas tingkat yang lebih tinggi, di mana Anda ingin abstraksi dari implementasi.

Perhatikan bahwa dengan kelas Anda mendapatkan antarmuka gratis. Selain implementasi, kelas mendefinisikan antarmuka dari set metode publik. Antarmuka itu diimplementasikan oleh semua kelas turunan. Ini bukan sepenuhnya antarmuka, tetapi dapat digunakan persis dengan cara yang sama. Jadi saya tidak berpikir perlu untuk membuat ulang antarmuka yang sudah ada dengan nama kelas.

Florian F
sumber