Apakah kebiasaan buruk tidak menggunakan antarmuka? [Tutup]

40

Saya jarang menggunakan antarmuka dan menemukan mereka umum dalam kode orang lain.

Saya juga membuat kelas sub dan super (sambil membuat kelas sendiri) jarang dalam kode saya.

  • Apakah itu hal yang buruk?
  • Apakah Anda menyarankan mengubah gaya ini?
  • Apakah gaya ini memiliki efek samping?
  • Apakah ini karena saya belum pernah mengerjakan proyek besar?
jineesh joseph
sumber
10
Bahasa apa ini?
2
Selain bahasa apa, paradigma apa ini?
Armando
1
Bukankah konsep "antarmuka" melampaui bahasa? Java memiliki tipe antarmuka bawaan, tetapi pengembang C ++ sering membuat antarmuka juga (mengawali mereka dengan I) dan mereka semua melayani tujuan yang sama.
Ricket
4
@Ricket: Tidak, tidak juga. Masalahnya adalah bahwa C ++ menawarkan pewarisan multi kelas. Dalam C ++ sebuah "antarmuka" jauh lebih konseptual dan pada kenyataannya kita sebagian besar menggunakan apa yang akan mereka definisikan sebagai kelas dasar abstrak. #
DeadMG
2
@Ricket no, terutama jika Anda menggunakan bahasa fungsional alih-alih bahasa prosedural atau OOP.
alternatif

Jawaban:

36

Ada beberapa alasan mengapa Anda mungkin ingin menggunakan antarmuka:

  1. Antarmuka cocok untuk situasi di mana aplikasi Anda membutuhkan banyak jenis objek yang mungkin tidak terkait untuk menyediakan fungsionalitas tertentu.
  2. Antarmuka lebih fleksibel daripada kelas dasar karena Anda dapat menentukan implementasi tunggal yang dapat mengimplementasikan banyak antarmuka.
  3. Antarmuka lebih baik dalam situasi di mana Anda tidak perlu mewarisi implementasi dari kelas dasar.
  4. 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.

http://msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80).aspx

Antarmuka seperti hal lain dalam pemrograman. Jika Anda tidak membutuhkannya, jangan menggunakannya. Saya telah melihat mereka digunakan secara luas sebagai masalah gaya, tetapi jika Anda tidak membutuhkan properti dan kemampuan khusus yang disediakan oleh antarmuka, saya tidak melihat manfaat menggunakannya "hanya karena".

Robert Harvey
sumber
13
Bahasanya tidak ditentukan, dan bahasa Anda terlalu spesifik - misalnya, dalam C ++, struktur memang dapat mewarisi dari kelas, dan Anda dapat melipatgandakan mewarisi basis non-abstrak.
DeadMG
2
Jawaban ini tampaknya adalah C # tetapi juga berkaitan dengan Java jika Anda mengambil # 4, dan hanya semacam berlaku untuk C ++ karena tentu saja pewarisan berganda diperbolehkan dalam C ++.
Ricket
2
Juga, saya tidak setuju dengan pernyataan terakhir. Saya pikir ada banyak praktik baik yang harus dilakukan oleh programmer tetapi tidak karena mereka merasa mereka tidak membutuhkannya. Saya pikir penanya sangat bijak dalam melihat-lihat, melihat orang lain melakukan hal ini, dan berpikir mungkin dia juga harus melakukannya tetapi bertanya "mengapa" terlebih dahulu.
Ricket
3
@Ricket: Mengapa ada dalam empat poin utama dari jawaban saya. Jika tidak ada alasan yang berlaku, maka Anda tidak perlu antarmuka.
Robert Harvey
17

Warisan kelas dan antarmuka keduanya memiliki tempat masing-masing. Warisan berarti "adalah" sementara antarmuka memberikan kontrak yang mendefinisikan seperti apa sesuatu "berperilaku".

Saya akan mengatakan bahwa menggunakan antarmuka lebih sering bukanlah praktik yang buruk sama sekali. Saya sedang membaca "Efektif C # - 50 Cara Tertentu untuk Meningkatkan C #" Anda oleh Bill Wagner. Item nomor 22 menyatakan, dan kutipan saya, "Memilih Menentukan dan Menerapkan Antarmuka ke Warisan".

Secara umum saya menggunakan kelas dasar ketika saya perlu mendefinisikan implementasi spesifik dari perilaku umum antara tipe yang berhubungan secara konseptual. Lebih sering saya menggunakan antarmuka. Bahkan, saya biasanya memulai dengan mendefinisikan antarmuka untuk kelas ketika saya mulai membuat satu ... bahkan jika pada akhirnya saya tidak mengkompilasi antarmuka, saya menemukan bahwa itu membantu untuk memulai dengan mendefinisikan API publik dari kelas dari mulai. Jika saya menemukan bahwa saya memiliki beberapa kelas yang mengimplementasikan antarmuka, dan logika implementasi identik, hanya kemudian saya akan bertanya pada diri sendiri apakah masuk akal untuk mengimplementasikan kelas dasar yang umum di antara kedua tipe tersebut.

Beberapa kutipan dari buku Bill Wagners ...

semua kelas turunan segera menggabungkan perilaku itu. Menambahkan anggota ke antarmuka memecah semua kelas yang mengimplementasikan antarmuka itu. Mereka tidak akan berisi metode baru dan tidak akan lagi dikompilasi. Setiap pelaksana harus memperbarui jenis itu untuk memasukkan anggota baru. Memilih antara kelas dasar abstrak dan antarmuka adalah pertanyaan tentang cara terbaik untuk mendukung abstraksi Anda dari waktu ke waktu. Antarmuka diperbaiki: Anda merilis antarmuka sebagai kontrak untuk serangkaian fungsi yang dapat diterapkan oleh semua jenis. Kelas dasar dapat diperpanjang dari waktu ke waktu. Ekstensi itu menjadi bagian dari setiap kelas turunan. Kedua model dapat dicampur untuk menggunakan kembali kode implementasi sambil mendukung beberapa antarmuka. " Mereka tidak akan berisi metode baru dan tidak akan lagi dikompilasi. Setiap pelaksana harus memperbarui jenis itu untuk memasukkan anggota baru. Memilih antara kelas dasar abstrak dan antarmuka adalah pertanyaan tentang cara terbaik untuk mendukung abstraksi Anda dari waktu ke waktu. Antarmuka diperbaiki: Anda merilis antarmuka sebagai kontrak untuk serangkaian fungsi yang dapat diterapkan oleh semua jenis. Kelas dasar dapat diperpanjang dari waktu ke waktu. Ekstensi itu menjadi bagian dari setiap kelas turunan. Kedua model dapat dicampur untuk menggunakan kembali kode implementasi sambil mendukung beberapa antarmuka. " Mereka tidak akan berisi metode baru dan tidak akan lagi dikompilasi. Setiap pelaksana harus memperbarui jenis itu untuk memasukkan anggota baru. Memilih antara kelas dasar abstrak dan antarmuka adalah pertanyaan tentang cara terbaik untuk mendukung abstraksi Anda dari waktu ke waktu. Antarmuka diperbaiki: Anda merilis antarmuka sebagai kontrak untuk serangkaian fungsi yang dapat diterapkan oleh semua jenis. Kelas dasar dapat diperpanjang dari waktu ke waktu. Ekstensi itu menjadi bagian dari setiap kelas turunan. Kedua model dapat dicampur untuk menggunakan kembali kode implementasi sambil mendukung beberapa antarmuka. " Anda merilis antarmuka sebagai kontrak untuk serangkaian fungsi yang dapat diterapkan oleh semua jenis. Kelas dasar dapat diperpanjang dari waktu ke waktu. Ekstensi itu menjadi bagian dari setiap kelas turunan. Kedua model dapat dicampur untuk menggunakan kembali kode implementasi sambil mendukung beberapa antarmuka. " Anda merilis antarmuka sebagai kontrak untuk serangkaian fungsi yang dapat diterapkan oleh semua jenis. Kelas dasar dapat diperpanjang dari waktu ke waktu. Ekstensi itu menjadi bagian dari setiap kelas turunan. Kedua model dapat dicampur untuk menggunakan kembali kode implementasi sambil mendukung beberapa antarmuka. "

"Antarmuka pengkodean memberikan fleksibilitas yang lebih besar untuk pengembang lain daripada pengkodean ke tipe kelas dasar."

"Menggunakan antarmuka untuk mendefinisikan API untuk suatu kelas memberikan fleksibilitas yang lebih besar."

"Ketika tipe Anda mengekspos properti sebagai tipe kelas, itu mengekspos seluruh antarmuka ke kelas itu. Menggunakan antarmuka, Anda dapat memilih untuk mengekspos hanya metode dan properti yang Anda ingin klien gunakan."

"Kelas dasar menggambarkan dan menerapkan perilaku umum di seluruh jenis beton terkait. Antarmuka menggambarkan potongan-potongan fungsionalitas atom yang dapat diterapkan oleh jenis beton yang tidak terkait. Keduanya memiliki tempat masing-masing. Kelas menentukan jenis yang Anda buat. Antarmuka menggambarkan perilaku jenis-jenis itu sebagai bagian dari fungsionalitas. Jika Anda memahami perbedaannya, Anda akan membuat desain yang lebih ekspresif yang lebih tangguh dalam menghadapi perubahan. Gunakan hierarki kelas untuk mendefinisikan tipe terkait. Paparkan fungsionalitas menggunakan antarmuka yang diimplementasikan pada semua tipe itu. "

John Connelly
sumber
2
Yah, saya membacanya dan berpikir itu adalah jawaban yang bagus.
Eric King
1
@ mc10 Tidak yakin apa masalahnya. Dia mereferensikan sebuah buku dan bagian bawah jawabannya adalah hanya kutipan dari buku itu yang dia jelaskan memberi tahu Anda jika Anda tidak ingin membuang waktu Anda.
Pete
@ Pete Saya tidak mengatakan bahwa jawabannya buruk, tetapi hanya bahwa itu agak terlalu lama. Saya ragu bahwa Anda kadang-kadang membutuhkan seluruh kutipan.
kevinji
3
haha, jika ini tl; dr, saya tidak yakin Anda akan menyukai seluruh jawaban berkualitas tinggi StackExchange ini.
Nic
haha, terima kasih kawan. Ya memang terpikir oleh saya bahwa jawabannya agak panjang lebar, tapi saya pikir saya akan memenuhi syarat jawaban saya dengan memasukkan kutipan yang relevan dari Tn. Wagner yang menguraikan kelebihan dan kekurangan antarmuka vs warisan, serta skenario di mana masing-masing lebih tepat. Mungkin mengutip buku itu secara langsung tidak menambah banyak nilai pada jawaban saya.
John Connelly
8

Satu hal yang belum disebutkan adalah pengujian: di semua C # mocking-libraries, dan juga beberapa untuk Java, kelas tidak dapat diejek kecuali mereka mengimplementasikan antarmuka. Hal ini menyebabkan banyak proyek mengikuti praktik Agile / TDD untuk memberikan setiap kelas antarmuka sendiri.

Beberapa orang menganggap praktik terbaik ini, karena "mengurangi kopling," tapi saya tidak setuju - saya pikir itu hanya solusi untuk kekurangan dalam bahasa.


Saya pikir antarmuka paling baik digunakan ketika Anda memiliki dua kelas atau lebih yang, secara abstrak, melakukan "hal yang sama," tetapi dengan cara yang berbeda.

Misalnya, kerangka kerja .Net memiliki beberapa kelas yang menyimpan daftar barang , tetapi mereka semua menyimpan barang itu dengan cara yang berbeda. Jadi, masuk akal untuk memiliki IList<T>antarmuka abstrak , yang dapat diimplementasikan menggunakan metode yang berbeda.

Anda juga harus menggunakannya ketika Anda ingin 2+ kelas dapat dipertukarkan, atau diganti di masa depan. Jika cara baru menyimpan daftar keluar di masa depan,, AwesomeList<T>dengan asumsi Anda menggunakan IList<T>seluruh kode Anda, mengubahnya dengan menggunakan AwesomeList<T>berarti hanya mengubah beberapa lusin baris, bukan beberapa ratus / ribu.

BlueRaja - Danny Pflughoeft
sumber
5

Hasil utama dari tidak menggunakan warisan dan antarmuka ketika mereka sesuai adalah kopling ketat . Ini mungkin agak sulit dipelajari untuk dikenali, tetapi biasanya gejala yang paling jelas adalah ketika Anda membuat perubahan, Anda menemukan bahwa Anda sering harus melalui banyak file lain untuk membuat perubahan dalam efek riak.

Karl Bielefeldt
sumber
4

Tidak, ini tidak buruk sama sekali. Antarmuka eksplisit harus digunakan, jika dan hanya jika, dua kelas dengan antarmuka umum harus dapat dipertukarkan pada saat run-time. Jika mereka tidak perlu dipertukarkan, maka jangan memilikinya. Sesederhana itu. Warisan rapuh dan harus dihindari sedapat mungkin. Jika Anda dapat menghindarinya atau menggunakan obat generik sebagai gantinya, maka lakukanlah.

Masalahnya adalah, dalam bahasa seperti C # dan Java dengan generik waktu kompilasi yang lemah, Anda dapat akhirnya melanggar KERING karena tidak ada cara untuk menulis satu metode yang dapat menangani lebih dari satu kelas kecuali semua kelas mewarisi dari basis yang sama. C # 4 dynamic dapat menangani hal ini.

Masalahnya, warisan seperti variabel global - begitu Anda menambahkannya dan kode Anda bergantung padanya, Tuhan membantu Anda mengambilnya. Namun, Anda dapat menambahkannya kapan saja, dan Anda bahkan dapat menambahkannya tanpa mengubah kelas dasar dengan menggunakan semacam pembungkus.

DeadMG
sumber
1
Alasan downvote?
DeadMG
Tidak yakin, dapatkan upvote. Itu jawaban yang bagus.
Bryan Boettcher
3

Ya, tidak (atau terlalu jarang) menggunakan antarmuka mungkin merupakan hal yang buruk. Antarmuka (dalam arti abstrak, dan konstruksi bahasa C # / Java adalah pendekatan yang cukup baik dari pengertian abstrak itu) mendefinisikan titik interaksi eksplisit antara sistem dan subsistem. Ini membantu mengurangi kopling dan membuat sistem lebih mudah dikelola. Seperti halnya apa pun yang meningkatkan rawatan, menjadi semakin penting semakin besar suatu sistem.

Michael Borgwardt
sumber
3

Saya belum pernah menggunakan antarmuka selama bertahun-tahun. Tentu saja ini karena saya telah memprogram hampir secara eksklusif di Erlang sekarang selama bertahun-tahun dan seluruh konsep antarmuka tidak ada. (Yang paling dekat Anda dapatkan adalah "perilaku" dan itu tidak benar-benar hal yang sama kecuali jika Anda menyipit sangat keras dan melihat mereka dari sudut mata Anda.)

Jadi, sungguh, pertanyaan Anda bergantung pada paradigma (OOP dalam kasus ini) dan, lebih jauh, sangat tergantung pada bahasa (ada bahasa OOP tanpa antarmuka).

HANYA PENDAPAT SAYA yang benar
sumber
2

Jika Anda berbicara tentang menggunakan Java, salah satu alasan untuk menggunakan antarmuka adalah bahwa mereka memungkinkan penggunaan objek proxy tanpa pustaka pembuatan kode. Ini bisa menjadi keuntungan besar ketika Anda bekerja dengan kerangka kerja yang kompleks seperti Spring. Selain itu, beberapa fungsionalitas memerlukan antarmuka: RMI adalah contoh klasik dari ini, karena Anda harus menggambarkan fungsionalitas yang Anda sediakan dalam hal antarmuka (yang diturunkan dari java.rmi.Remote) namun Anda harus mengimplementasikannya.

Donal Fellows
sumber
1

Hal-hal berubah ( MS Moles ), tetapi alasan utama yang menurut saya baik untuk kode hampir secara eksklusif untuk antarmuka adalah bahwa mereka mudah untuk diejek dan masuk secara alami ke dalam arsitektur IoC.

IMO Anda hanya boleh bekerja dengan antarmuka, atau DAO sepenuhnya bodoh jika memungkinkan. Setelah Anda masuk ke dalam pola pikir ini, dan Anda mulai menggunakan perpustakaan yang tidak mengekspos dirinya melalui antarmuka dan melakukan semuanya melalui objek konkret saya harus jujur, semuanya terasa agak kikuk.


sumber
0

Untuk proyek mode lama, orang menggunakan antarmuka untuk menghindari referensi melingkar, karena referensi melingkar bisa menjadi masalah pemeliharaan besar dalam jangka panjang.

Buruk:

class B; // forward declaration
class A
{
  B* b;
};

class B
{
  A* a;
}

Tidak buruk:

class PartOfClassB_AccessedByA
{
};

class A
{
  PartOfClassB_AccessedByA* b;
};

class B : public PartOfClassB_AccessedByA
{
  A* a;
}

Biasanya A, B, PartOfClassB_AccessedByA diimplementasikan dengan file terpisah.

9dan
sumber
0

Pemrograman berbasis antarmuka membantu membuat kode lebih fleksibel dan lebih mudah untuk diuji. Kelas implementasi dapat diubah tanpa menyentuh kode klien [Fleksibilitas]. Sementara pengujian kode satu dapat menggantikan pemrograman berbasis oInterface yang sebenarnya membantu membuat kode lebih fleksibel dan lebih mudah untuk diuji. Kelas implementasi dapat diubah tanpa menyentuh kode klien [Fleksibilitas]. Sementara pengujian satu kode dapat mengganti objek aktual dengan benda tiruan [Testability] .bject dengan benda tiruan [Testability].

Amit Patel
sumber