Antarmuka pada kelas abstrak

30

Rekan kerja saya dan saya memiliki pendapat berbeda tentang hubungan antara kelas dasar dan antarmuka. Saya berkeyakinan bahwa suatu kelas tidak boleh mengimplementasikan antarmuka kecuali kelas itu dapat digunakan ketika implementasi antarmuka diperlukan. Dengan kata lain, saya suka melihat kode seperti ini:

interface IFooWorker { void Work(); }

abstract class BaseWorker {
    ... base class behaviors ...
    public abstract void Work() { }
    protected string CleanData(string data) { ... }
}

class DbWorker : BaseWorker, IFooWorker {
    public void Work() {
        Repository.AddCleanData(base.CleanData(UI.GetDirtyData()));
    }
}

DbWorker adalah yang mendapat antarmuka IFooWorker, karena merupakan implementasi antarmuka instantiatable. Itu sepenuhnya memenuhi kontrak. Rekan kerja saya lebih suka yang hampir identik:

interface IFooWorker { void Work(); }

abstract class BaseWorker : IFooWorker {
    ... base class behaviors ...
    public abstract void Work() { }
    protected string CleanData(string data) { ... }
}

class DbWorker : BaseWorker {
    public void Work() {
        Repository.AddCleanData(base.CleanData(UI.GetDirtyData()));
    }
}

Di mana kelas dasar mendapatkan antarmuka, dan berdasarkan ini semua pewaris kelas dasar adalah antarmuka itu juga. Ini mengganggu saya tetapi saya tidak dapat menemukan alasan konkret mengapa, di luar "kelas dasar tidak dapat berdiri sendiri sebagai implementasi dari antarmuka".

Apa pro & kontra dari metodenya vs. tambang, dan mengapa seseorang harus digunakan di atas yang lain?

Bryan Boettcher
sumber
Saran Anda sangat mirip dengan warisan berlian , yang dapat menyebabkan banyak kebingungan lebih lanjut.
Spoike
@Spoike: Bagaimana cara melihatnya?
Niing
@Niing link yang saya berikan di komentar memiliki diagram kelas sederhana dan penjelasan satu-baris polos untuk apa itu. ini disebut masalah "intan" karena struktur warisan pada dasarnya digambar seperti bentuk intan (yaitu ♦ ♦).
Spoike
@Spoike: mengapa pertanyaan ini akan menyebabkan warisan berlian?
Niing
1
@Niing: Mewarisi BaseWorker dan IFooWorker dengan metode yang sama disebut Workadalah masalah warisan berlian (dalam hal itu mereka berdua memenuhi kontrak atas Workmetode ini). Di Jawa Anda harus mengimplementasikan metode antarmuka sehingga masalah Workmetode mana yang harus digunakan oleh program dihindari dengan cara itu. Namun, bahasa seperti C ++ tidak membingungkan Anda.
Spoike

Jawaban:

24

Saya berkeyakinan bahwa suatu kelas tidak boleh mengimplementasikan antarmuka kecuali kelas itu dapat digunakan ketika implementasi antarmuka diperlukan.

BaseWorkermemenuhi persyaratan itu. Hanya karena Anda tidak dapat secara langsung membuat instance BaseWorkerobjek tidak berarti Anda tidak dapat memiliki BaseWorker pointer yang memenuhi kontrak. Memang, itulah inti dari kelas abstrak.

Juga, sulit untuk mengetahui dari contoh sederhana yang Anda posting, tetapi bagian dari masalahnya mungkin antarmuka dan kelas abstraknya berlebihan. Kecuali Anda memiliki implementasi kelas lain IFooWorkeryang tidak berasal BaseWorker, Anda tidak memerlukan antarmuka sama sekali. Antarmuka hanyalah kelas abstrak dengan beberapa batasan tambahan yang membuat pewarisan berganda lebih mudah.

Sekali lagi sulit untuk mengetahui dari contoh sederhana, penggunaan metode yang dilindungi, secara eksplisit merujuk pada basis dari kelas turunan, dan kurangnya tempat yang tidak ambigu untuk mendeklarasikan implementasi antarmuka adalah tanda-tanda peringatan bahwa Anda secara tidak tepat menggunakan pewarisan alih-alih menggunakan komposisi. Tanpa warisan itu, seluruh pertanyaan Anda menjadi diperdebatkan.

Karl Bielefeldt
sumber
1
+1 untuk menunjukkan bahwa hanya karena BaseWorkertidak dapat dipakai secara langsung, tidak berarti yang diberikan BaseWorker myWorkertidak diterapkan IFooWorker.
Avner Shahar-Kashtan
2
Saya tidak setuju bahwa antarmuka hanyalah kelas abstrak. Kelas abstrak biasanya mendefinisikan beberapa detail implementasi (jika tidak antarmuka akan digunakan). Jika detail itu tidak sesuai dengan keinginan Anda, Anda sekarang harus mengubah setiap penggunaan kelas dasar menjadi sesuatu yang lain. Antarmuka dangkal; mereka mendefinisikan hubungan "plug-and-socket" antara dependensi dan tanggungan. Dengan demikian, ketat kopling untuk setiap detail implementasi tidak lagi perhatian. Jika kelas yang mewarisi antarmuka tidak sesuai dengan keinginan Anda, lepaskan dan masukkan sesuatu yang lain sepenuhnya; kode Anda tidak terlalu peduli.
KeithS
5
Mereka berdua memiliki kelebihan, tetapi biasanya antarmuka harus selalu digunakan untuk dependensi, apakah ada kelas abstrak yang mendefinisikan implementasi basis. Kombinasi antarmuka dan kelas abstrak memberikan yang terbaik dari kedua dunia; antarmuka mempertahankan hubungan plug-and-surface permukaan yang dangkal, sementara kelas abstrak menyediakan kode umum yang berguna. Anda dapat menghapus dan mengganti level apa pun di bawah antarmuka ini sesuka hati.
KeithS
Dengan "pointer", maksud Anda adalah instance dari objek (yang akan dirujuk oleh pointer)? Antarmuka bukan kelas, mereka adalah tipe, dan dapat bertindak sebagai pengganti yang tidak lengkap untuk pewarisan berganda, bukan pengganti - yaitu, mereka "termasuk perilaku dari berbagai sumber di kelas".
samis
46

Saya harus setuju dengan rekan kerja Anda.

Dalam kedua contoh yang Anda berikan, BaseWorker mendefinisikan metode abstrak Work (), yang berarti bahwa semua subclass mampu memenuhi kontrak IFooWorker. Dalam hal ini, saya pikir BaseWorker harus mengimplementasikan antarmuka, dan implementasi itu akan diwarisi oleh subkelasnya. Ini akan menyelamatkan Anda dari keharusan untuk secara eksplisit menunjukkan bahwa setiap subclass memang merupakan IFooWorker (prinsip KERING).

Jika Anda tidak mendefinisikan Work () sebagai metode BaseWorker, atau jika IFooWorker memiliki metode lain yang tidak diinginkan atau dibutuhkan subclass dari subclass dari BaseWorker, maka (jelas) Anda harus menunjukkan subclass mana yang benar-benar mengimplementasikan IFooWorker.

Matthew Flynn
sumber
11
+1 akan memposting hal yang sama. Maaf, tapi saya pikir rekan kerja Anda benar dalam kasus ini juga, jika sesuatu menjamin untuk memenuhi kontrak, itu harus mewarisi kontrak itu, apakah jaminan dipenuhi oleh antarmuka, kelas abstrak, atau kelas beton. Sederhananya: Penjamin harus mewarisi kontrak yang dijaminnya.
Jimmy Hoffa
2
Saya biasanya setuju, tetapi ingin menunjukkan bahwa kata-kata seperti "base", "standard", "default", "generic", dll. Adalah bau kode dalam nama kelas. Jika kelas dasar abstrak memiliki nama yang hampir sama dengan antarmuka tetapi dengan salah satu dari kata-kata musang yang dimasukkan, itu sering merupakan tanda abstraksi yang salah; antarmuka mendefinisikan apa sesuatu tidak , mendefinisikan jenis warisan apa yang . Jika Anda memiliki IFoodan BaseFookemudian mengimplikasikan bahwa keduanya IFoobukan antarmuka yang tepat, atau yang BaseFoomenggunakan warisan di mana komposisi mungkin merupakan pilihan yang lebih baik.
Aaronaught
@Aaronaught Poin yang bagus, meskipun mungkin ada sesuatu yang benar-benar diperlukan oleh semua atau sebagian besar implementasi IFoo (seperti pegangan untuk layanan atau implementasi DAO Anda).
Matthew Flynn
2
Maka bukankah seharusnya kelas dasar dipanggil MyDaoFooatau SqlFooatau HibernateFooatau apa pun, untuk menunjukkan bahwa itu hanya satu kemungkinan pohon kelas yang diimplementasikan IFoo? Bagi saya lebih bau jika kelas dasar digabungkan ke perpustakaan atau platform tertentu dan tidak disebutkan namanya dalam nama ...
Aaronaught
@Aaronaught - Ya. Aku sangat setuju.
Matthew Flynn
11

Saya biasanya setuju dengan rekan kerja Anda.

Mari kita ambil model Anda: antarmuka hanya diterapkan oleh kelas anak, meskipun kelas dasar juga memberlakukan pemaparan metode IFooWorker. Pertama, itu berlebihan; apakah kelas anak mengimplementasikan antarmuka atau tidak, mereka diharuskan untuk mengganti metode yang diekspos dari BaseWorker, dan juga setiap implementasi IFooWorker harus mengekspos fungsi apakah mereka mendapat bantuan dari BaseWorker atau tidak.

Ini juga membuat hierarki Anda ambigu; "Semua IFooWorkers adalah BaseWorkers" tidak selalu merupakan pernyataan yang benar, dan tidak juga "Semua BaseWorkers adalah IFooWorkers". Jadi, jika Anda ingin mendefinisikan variabel instan, parameter, atau tipe pengembalian yang bisa berupa implementasi dari IFooWorker atau BaseWorker (memanfaatkan fungsionalitas umum yang terbuka yang merupakan salah satu alasan untuk mewarisi di tempat pertama), tidak satu pun dari ini dijamin mencakup semua; beberapa BaseWorkers tidak akan dapat ditugaskan ke variabel tipe IFooWorker, dan sebaliknya.

Model rekan kerja Anda lebih mudah digunakan dan diganti. "Semua BaseWorkers adalah IFooWorkers" sekarang merupakan pernyataan yang benar; Anda dapat memberikan instance BaseWorker untuk ketergantungan IFooWorker, tanpa masalah. Pernyataan sebaliknya "Semua IFooWorkers adalah BaseWorkers" tidak benar; yang memungkinkan Anda untuk mengganti BaseWorker dengan BetterBaseWorker dan kode konsumsi Anda (yang tergantung pada IFooWorker) tidak perlu memberi tahu perbedaannya.

KeithS
sumber
Saya suka suara jawaban ini tetapi tidak cukup mengikuti bagian terakhir. Anda menyarankan bahwa BetterBaseWorker akan menjadi kelas abstrak independen yang mengimplementasikan IFooWorker, sehingga plugin hipotetis saya bisa dengan mudah mengatakan "oh nak! Pekerja Pangkalan Lebih Baik akhirnya keluar!" dan ubah referensi BaseWorker untuk BetterBaseWorker dan sebut itu sehari (mungkin bermain dengan nilai-nilai baru yang dikembalikan keren yang memungkinkan saya menghapus potongan besar kode redundan saya, dll)?
Anthony
Lebih atau kurang, ya. Keuntungan besar adalah Anda akan mengurangi jumlah referensi ke BaseWorker yang harus diubah menjadi BetterBaseWorkers sebagai gantinya; sebagian besar kode Anda tidak akan mereferensikan kelas beton secara langsung, alih-alih menggunakan IFooWorker, jadi ketika Anda mengubah apa yang ditugaskan untuk dependensi IFooWorker tersebut (properti kelas, parameter konstruktor atau metode) kode yang menggunakan IFooWorker tidak akan melihat perbedaan dalam penggunaan.
KeithS
6

Saya perlu menambahkan peringatan untuk jawaban ini. Menggabungkan kelas dasar ke antarmuka menciptakan kekuatan dalam struktur kelas itu. Dalam contoh dasar Anda, tidak ada keraguan bahwa keduanya harus digabungkan, tetapi itu mungkin tidak berlaku dalam semua kasus.

Ambil kelas framework koleksi Java:

abstract class AbstractList
class LinkedList extends AbstractList implements Queue

Fakta bahwa kontrak Antrian dilaksanakan oleh LinkedList tidak mendorong kekhawatiran ke AbstractList .

Apa perbedaan antara kedua kasus itu? Tujuan BaseWorker selalu (seperti dikomunikasikan dengan nama dan antarmuka) untuk mengimplementasikan operasi di IWorker . Tujuan AbstractList dan Queue berbeda, tetapi keturunan dari yang lama masih dapat mengimplementasikan kontrak yang terakhir.

Mihai Danila
sumber
Ini terjadi separuh waktu. Dia selalu memilih untuk mengimplementasikan antarmuka pada kelas dasar, dan saya selalu lebih suka untuk mengimplementasikannya pada beton akhir. Skenario yang Anda sajikan sering terjadi dan merupakan bagian dari alasan antarmuka di pangkalan mengganggu saya.
Bryan Boettcher
1
Benar, Insta, dan saya menganggap penggunaan antarmuka dengan cara ini sebagai bagian dari warisan yang terlalu banyak kita lihat di banyak lingkungan pengembangan. Seperti yang GoF katakan dalam buku pola desain mereka, lebih suka komposisi daripada warisan , dan menjaga antarmuka dari kelas dasar adalah salah satu cara mempromosikan prinsip itu.
Mihai Danila
1
Saya mendapatkan peringatan, tetapi Anda akan mencatat bahwa kelas abstrak OP termasuk metode abstrak yang cocok dengan antarmuka: BaseWorker secara implisit merupakan IFooWorker. Membuatnya eksplisit membuat fakta ini lebih bermanfaat. Ada banyak metode yang termasuk dalam Antrian yang tidak termasuk dalam AbstractList, namun, dan dengan demikian, AbstractList bukanlah sebuah Antrian, bahkan jika anak-anaknya bisa. AbstractList dan Antrian bersifat orthogonal.
Matthew Flynn
Terima kasih atas wawasan ini; Saya setuju bahwa kasus OP jatuh ke dalam kategori itu, tetapi saya merasa ada diskusi yang lebih besar dalam mencari aturan yang lebih dalam, dan saya ingin membagikan pengamatan saya tentang kecenderungan yang saya baca (dan bahkan saya sendiri pernah untuk waktu yang singkat) dari pewarisan yang berlebihan, mungkin karena itulah salah satu alat di kotak peralatan OOP.
Mihai Danila
Dang, sudah enam tahun. :)
Mihai Danila
2

Saya akan bertanya, apa yang terjadi ketika Anda berubah IFooWorker, seperti menambahkan metode baru?

Jika BaseWorkermengimplementasikan antarmuka, secara default ia harus mendeklarasikan metode baru, bahkan jika itu abstrak. Di sisi lain, jika tidak mengimplementasikan antarmuka, Anda hanya akan mendapatkan kesalahan kompilasi pada kelas turunan.

Untuk alasan itu, saya akan membuat kelas dasar mengimplementasikan antarmuka , karena saya mungkin dapat menerapkan semua fungsi untuk metode baru di kelas dasar tanpa menyentuh kelas turunan.

Scott Whitlock
sumber
1

Pertama-tama pikirkan apa itu kelas dasar abstrak, dan apa antarmuka itu. Pertimbangkan kapan Anda akan menggunakan satu atau yang lain dan kapan Anda tidak.

Sudah umum bagi orang untuk berpikir bahwa keduanya adalah konsep yang sangat mirip, bahkan itu adalah pertanyaan wawancara yang umum (perbedaan antara keduanya adalah ??)

Jadi kelas dasar abstrak memberi Anda sesuatu yang tidak bisa antarmuka yang merupakan implementasi metode standar. (Ada hal lain, di C # Anda dapat memiliki metode statis pada kelas abstrak, bukan pada antarmuka misalnya).

Sebagai contoh, salah satu penggunaan umum ini adalah dengan IDisposable. Kelas abstrak mengimplementasikan metode Buang IDisposable, yang berarti setiap versi turunan dari kelas dasar akan secara otomatis dibuang. Anda kemudian dapat bermain dengan beberapa opsi. Buat Buang abstrak, memaksa kelas turunan untuk mengimplementasikannya. Berikan implementasi default virtual, jadi mereka tidak melakukannya, atau membuatnya bukan virtual atau abstrak dan minta metode virtual yang disebut hal-hal seperti BeforeDispose, AfterDispose, OnDispose atau Disposing misalnya.

Jadi, setiap saat semua kelas turunan perlu mendukung antarmuka, itu akan berlaku pada kelas dasar. Jika hanya satu atau beberapa yang perlu antarmuka itu akan pergi pada kelas turunan.

Itu semua sebenarnya terlalu disederhanakan. Alternatif lain adalah memiliki kelas turunan yang bahkan tidak mengimplementasikan antarmuka, tetapi menyediakannya melalui pola adaptor. Contoh dari ini saya melihat baru-baru ini di IObjectContextAdapter.

Ian
sumber
1

Saya masih bertahun-tahun lagi untuk sepenuhnya memahami perbedaan antara kelas abstrak dan antarmuka. Setiap kali saya pikir saya memahami konsep dasar, saya mencari stackexchange dan saya mundur dua langkah. Tetapi beberapa pemikiran tentang topik dan pertanyaan OP:

Pertama:

Ada dua penjelasan umum antarmuka:

  1. Antarmuka adalah daftar metode dan properti yang dapat diterapkan oleh setiap kelas, dan dengan mengimplementasikan antarmuka, kelas menjamin metode tersebut (dan tanda tangannya) dan properti tersebut (dan tipenya) akan tersedia ketika "berinteraksi" dengan kelas itu atau sebuah objek dari kelas itu. Antarmuka adalah kontrak.

  2. Antarmuka adalah kelas abstrak yang tidak / tidak bisa melakukan apa pun. Mereka berguna karena Anda dapat menerapkan lebih dari satu, tidak seperti kelas induk yang berarti. Seperti, saya bisa menjadi objek kelas BananaBread, dan mewarisi dari BaseBread, tetapi itu tidak berarti saya tidak bisa juga mengimplementasikan antarmuka IWithNuts dan ITastesYummy. Saya bahkan bisa mengimplementasikan antarmuka IDoesTheDishes, karena saya bukan hanya roti, tahu?

Ada dua penjelasan umum tentang kelas abstrak:

  1. Kelas abstrak, yah, adalah hal yang tidak bisa dilakukan oleh hal itu. Ini seperti, intinya, sama sekali bukan hal yang nyata. Tunggu, ini akan membantu. Kapal adalah kelas abstrak, tetapi Playboy Yacht yang seksi akan menjadi sub kelas dari BaseBoat.

  2. Saya membaca buku tentang kelas abstrak, dan mungkin Anda harus membaca buku itu, karena Anda mungkin tidak mengerti dan akan melakukannya dengan salah jika Anda belum membaca buku itu.

Ngomong-ngomong, buku Citers selalu tampak mengesankan, walaupun aku masih berjalan pergi dengan bingung.

Kedua:

Pada SO, seseorang menanyakan versi sederhana dari pertanyaan ini, klasik, "mengapa menggunakan antarmuka? Apa bedanya? Apa yang saya lewatkan?" Dan satu jawaban menggunakan pilot angkatan udara sebagai contoh sederhana. Itu tidak cukup mendarat, tetapi memicu beberapa komentar yang bagus, salah satunya menyebutkan antarmuka IFlyable memiliki metode seperti takeOff, pilotEject, dll. Dan ini benar-benar diklik bagi saya sebagai contoh dunia nyata mengapa antarmuka tidak hanya berguna, tetapi sangat penting. Antarmuka membuat objek / kelas menjadi intuitif, atau paling tidak memberikan pengertian bahwa itu objek. Antarmuka bukan untuk kepentingan objek atau data, tetapi untuk sesuatu yang perlu berinteraksi dengan objek itu. Buah klasik-> Apple-> Fuji atau Shape-> Triangle-> Contoh-contoh pewarisan sama sisi adalah model yang bagus untuk memahami taksonomi objek yang diberikan berdasarkan keturunannya. Ini memberi tahu konsumen dan prosesor tentang kualitas umum, perilaku, apakah objek itu sekelompok hal, akan menyiram sistem Anda jika Anda meletakkannya di tempat yang salah, atau menjelaskan data sensorik, konektor ke toko data tertentu, atau data keuangan diperlukan untuk membuat daftar gaji.

Objek Plane tertentu mungkin atau mungkin tidak memiliki metode untuk pendaratan darurat, tapi saya akan marah jika saya menganggap daruratnya. karena mereka mengira semuanya sama saja. Sama seperti bagaimana saya akan frustrasi jika setiap produsen sekrup memiliki interpretasi yang benar tentang fasty tighty atau jika TV saya tidak memiliki tombol kenaikan volume di atas tombol penurunan volume.

Kelas abstrak adalah model yang menetapkan apa yang harus dimiliki objek turunan apa pun untuk dikualifikasikan sebagai kelas itu. Jika Anda tidak memiliki semua kualitas bebek, tidak masalah bahwa Anda memiliki antarmuka IQuack diimplementasikan, Anda hanya seekor penguin aneh. Antarmuka adalah hal-hal yang masuk akal bahkan ketika Anda tidak bisa memastikan hal lain. Jeff Goldblum dan Starbuck sama-sama dapat menerbangkan pesawat ruang angkasa alien karena antarmuka yang mirip.

Ketiga:

Saya setuju dengan rekan kerja Anda, karena kadang-kadang Anda perlu menerapkan metode tertentu di awal. Jika Anda membuat ORM Rekaman Aktif, ini membutuhkan metode penyimpanan. Ini tidak sampai ke subkelas yang bisa dipakai. Dan jika antarmuka ICRUD cukup portabel untuk tidak secara eksklusif digabungkan ke satu kelas abstrak, itu dapat diimplementasikan oleh kelas lain untuk membuatnya dapat diandalkan dan intuitif bagi siapa pun yang sudah akrab dengan salah satu kelas turunan dari kelas abstrak itu.

Di sisi lain, ada contoh yang bagus sebelumnya ketika tidak melompat untuk mengikat antarmuka ke kelas abstrak, karena tidak semua tipe daftar akan (atau seharusnya) mengimplementasikan antarmuka antrian. Anda mengatakan skenario ini terjadi separuh waktu, yang berarti Anda dan rekan kerja Anda sama-sama salah separuh waktu, dan dengan demikian hal terbaik untuk dilakukan adalah berdebat, berdebat, mempertimbangkan, dan jika ternyata benar, akui dan terima sambungan tersebut. . Tetapi jangan menjadi pengembang yang mengikuti filosofi bahkan ketika itu bukan yang terbaik untuk pekerjaan yang ada.

Anthony
sumber
0

Ada aspek lain mengapa DbWorkerharus menerapkan IFooWorker, seperti yang Anda sarankan.

Dalam hal gaya rekan kerja Anda, jika karena alasan tertentu terjadi refactoring kemudian dan DbWorkerdianggap tidak memperpanjang BaseWorkerkelas abstrak , DbWorkerkehilangan IFooWorkerantarmuka. Ini bisa, tetapi tidak harus, berdampak pada klien yang memakannya, jika mereka mengharapkan IFooWorkerantarmuka.

Frederik Krautwald
sumber
-1

Untuk memulai, saya ingin mendefinisikan antarmuka dan kelas abstrak secara berbeda:

Interface: a contract that each class must fulfill if they are to implement that interface
Abstract class: a general class (i.e vehicle is an abstract class, while porche911 is not)

Dalam kasus Anda, semua pekerja menerapkan work()karena itulah yang diwajibkan oleh kontrak bagi mereka. Oleh karena itu kelas dasar Anda Baseworkerharus mengimplementasikan metode itu baik secara eksplisit atau sebagai fungsi virtual. Saya menyarankan Anda untuk meletakkan metode ini di kelas dasar Anda karena fakta sederhana bahwa semua pekerja perlu work(). Oleh karena itu logis bahwa baseclass Anda (meskipun Anda tidak dapat membuat pointer dari tipe itu) memasukkannya sebagai fungsi.

Sekarang, DbWorkermemang memenuhi IFooWorkerantarmuka, tetapi jika ada cara khusus yang kelas ini lakukan work(), maka itu benar-benar perlu membebani definisi yang diwarisi BaseWorker. Alasan tidak seharusnya mengimplementasikan antarmuka IFooWorkersecara langsung adalah karena ini bukan sesuatu yang istimewa DbWorker. Jika Anda melakukan itu setiap kali Anda menerapkan kelas yang sama DbWorkermaka Anda akan melanggar KERING .

Jika dua kelas menerapkan fungsi yang sama dengan cara yang berbeda, Anda bisa mulai mencari superclass umum terbesar. Sebagian besar waktu Anda akan menemukan satu. Jika tidak, teruslah mencari atau menyerah dan menerima bahwa mereka tidak memiliki kesamaan dalam hal membuat kelas dasar.

Arnab Datta
sumber
-3

Wow, semua jawaban ini dan tidak ada yang menunjukkan bahwa antarmuka dalam contoh ini tidak memiliki tujuan apa pun. Anda tidak menggunakan warisan dan antarmuka untuk metode yang sama, itu tidak ada gunanya. Anda menggunakan satu atau yang lain. Ada banyak pertanyaan di forum ini dengan jawaban yang menjelaskan manfaat masing-masing dan senarios yang membutuhkan satu atau yang lain.

Martin Maat
sumber
3
Itu kepalsuan paten. Antarmuka adalah kontrak di mana sistem diharapkan berperilaku, tanpa memperhatikan implementasinya. Kelas dasar yang diwariskan adalah salah satu dari banyak kemungkinan implementasi untuk antarmuka ini. Sistem skala yang cukup besar akan memiliki beberapa kelas dasar yang memenuhi berbagai antarmuka (biasanya ditutup dengan obat generik), yang persis seperti yang dilakukan pengaturan ini dan mengapa sangat membantu untuk memecahnya.
Bryan Boettcher
Ini adalah contoh yang sangat buruk dari perspektif OOP. Seperti yang Anda katakan "kelas dasar hanya satu implementasi" (seharusnya atau tidak akan ada gunanya untuk antarmuka), Anda mengatakan akan ada kelas non-pekerja yang mengimplementasikan antarmuka IFooWorker. Maka Anda akan memiliki kelas dasar yang merupakan fooworker khusus (kita harus berasumsi mungkin ada pekerja bar juga) dan Anda akan memiliki fooworker lain yang bahkan bukan pekerja dasar! Itu terbelakang. Lebih dari satu cara.
Martin Maat
1
Mungkin Anda terjebak pada kata "Pekerja" di antarmuka. Bagaimana dengan "Sumber Data"? Jika kami memiliki IDatasource <T>, kami dapat dengan mudah memiliki SqlDatasource <T>, RestDatasource <T>, MongoDatasource <T>. Bisnis memutuskan penutupan mana dari antarmuka generik menuju implementasi penutupan dari sumber data abstrak.
Bryan Boettcher
Masuk akal untuk menggunakan warisan hanya demi KERING dan meletakkan beberapa implementasi umum di kelas dasar. Tetapi Anda tidak akan menyelaraskan metode yang diganti dengan metode antarmuka apa pun, kelas dasar akan berisi logika dukungan umum. Jika mereka berbaris, itu akan menunjukkan warisan menyelesaikan masalah Anda dengan baik dan antarmuka tidak akan berfungsi. Anda menggunakan antarmuka dalam kasus warisan tidak menyelesaikan masalah Anda karena karakteristik umum yang Anda ingin model tidak cocok dengan pohon kelas singke. Dan ya, kata-kata dalam contoh membuatnya lebih salah.
Martin Maat