Apakah "Jika suatu metode digunakan kembali tanpa perubahan, letakkan metode tersebut di kelas dasar, selain itu membuat antarmuka" aturan yang baik?

10

Seorang kolega saya datang dengan aturan praktis untuk memilih antara membuat kelas dasar atau antarmuka.

Dia berkata:

Bayangkan setiap metode baru yang akan Anda implementasikan. Untuk masing-masing dari mereka, pertimbangkan ini: akankah metode ini diterapkan oleh lebih dari satu kelas dalam bentuk yang persis seperti ini, tanpa perubahan apa pun ? Jika jawabannya "ya", buat kelas dasar. Dalam setiap situasi lain, buat antarmuka.

Sebagai contoh:

Pertimbangkan kelas catdan dog, yang memperluas kelas mammaldan memiliki metode tunggal pet(). Kami kemudian menambahkan kelas alligator, yang tidak memperpanjang apa pun dan memiliki metode tunggal slither().

Sekarang, kami ingin menambahkan eat()metode untuk semuanya.

Jika implementasi eat()metode akan persis sama untuk cat, dogdan alligator, kita harus membuat kelas dasar (katakanlah, animal), yang mengimplementasikan metode ini.

Namun, jika implementasi alligatorberbeda sedikit, kita harus membuat IEatantarmuka dan membuat mammaldan alligatormengimplementasikannya.

Dia bersikeras bahwa metode ini mencakup semua kasus, tetapi sepertinya terlalu menyederhanakan bagi saya.

Apakah layak mengikuti aturan praktis ini?

sbichenko
sumber
27
alligatorImplementasi dari eatperbedaan, tentu saja, dalam hal ia menerima catdan dogsebagai parameter.
sbichenko
1
Di mana Anda benar-benar ingin kelas dasar abstrak untuk berbagi implementasi, tetapi harus menggunakan antarmuka untuk ekstensibilitas yang benar, Anda sering dapat menggunakan ciri-ciri . Yaitu, jika bahasa Anda mendukung ini.
amon
2
Saat Anda melukis diri sendiri di sudut, yang terbaik adalah berada di dekat pintu.
JeffO
10
Kesalahan terbesar yang dilakukan orang adalah percaya bahwa antarmuka hanyalah kelas abstrak yang kosong. Antarmuka adalah cara bagi seorang programmer untuk mengatakan, "Saya tidak peduli apa yang Anda berikan kepada saya, asalkan mengikuti konvensi ini." Penggunaan kembali kode kemudian harus dicapai (idealnya) melalui komposisi. Rekan kerja Anda salah.
riwalk
2
Profesi CS saya mengajarkan bahwa superclasses seharusnya is adan interface adalah acts likeatau is. Jadi is amamalia dan acts likepemakan anjing . Ini akan memberi tahu kita bahwa mamalia harus kelas dan pemakan harus antarmuka. Itu selalu menjadi panduan yang sangat membantu. Sidenote: Contoh isakan The cake is eatableatau The book is writable.
MirroredFate

Jawaban:

13

Saya tidak berpikir bahwa ini adalah aturan praktis yang baik. Jika Anda khawatir tentang penggunaan kembali kode, Anda dapat menerapkan PetEatingBehaviorperan yang menentukan fungsi makan untuk kucing dan anjing. Kemudian Anda dapat memiliki IEatdan menggunakan kembali kode bersama.

Hari-hari ini saya melihat semakin sedikit alasan untuk menggunakan warisan. Satu keuntungan besar adalah kemudahan penggunaan. Ambil kerangka kerja GUI. Cara populer untuk merancang API untuk ini adalah dengan mengekspos kelas dasar yang besar, dan mendokumentasikan metode mana yang dapat ditimpa pengguna. Jadi kita dapat mengabaikan semua hal rumit lain yang perlu dikelola aplikasi kita, karena implementasi "default" diberikan oleh kelas dasar. Tetapi kita masih dapat menyesuaikan hal-hal dengan menerapkan kembali metode yang benar ketika kita perlu.

Jika Anda tetap menggunakan antarmuka untuk api Anda, pengguna Anda biasanya memiliki lebih banyak pekerjaan yang harus dilakukan untuk membuat contoh sederhana berfungsi. Tetapi API dengan antarmuka seringkali kurang digabungkan dan lebih mudah dipelihara karena alasan sederhana yang IEatmengandung lebih sedikit informasi daripada Mammalkelas dasar. Jadi konsumen akan bergantung pada kontrak yang lebih lemah, Anda mendapatkan lebih banyak peluang refactoring, dll.

Mengutip Rich Hickey: Sederhana! = Mudah

Simon Bergot
sumber
Bisakah Anda memberikan contoh dasar PetEatingBehaviorimplementasi?
sbichenko
1
@ express Pertama, saya buruk dalam memilih nama. Jadi PetEatingBehaviormungkin yang salah. Saya tidak bisa memberi Anda implementasi karena ini hanya contoh mainan. Tapi saya bisa menjelaskan langkah-langkah refactoring: ia memiliki konstruktor yang mengambil semua informasi yang digunakan oleh kucing / anjing untuk menentukan eatmetode (contoh gigi, contoh perut, dll). Ini hanya berisi kode umum untuk kucing dan anjing yang digunakan dalam eatmetode mereka . Anda hanya perlu membuat PetEatingBehavioranggota dan meneruskannya panggilan ke dog.eat/cat.eat.
Simon Bergot
Saya tidak percaya ini adalah satu-satunya jawaban dari banyak orang untuk menjauhkan OP dari warisan :( Warisan bukan untuk penggunaan kembali kode!
Danny Tuppeny
1
@DannyTuppeny Kenapa tidak?
sbichenko
4
@exizt Mewarisi dari sebuah kelas adalah masalah besar; Anda mengambil ketergantungan (kemungkinan-permanen) pada sesuatu, yang dalam banyak bahasa Anda hanya dapat memilikinya. Penggunaan kembali kode adalah hal yang cukup sepele untuk menggunakan kelas dasar yang sering hanya ini. Bagaimana jika Anda berakhir dengan metode di mana Anda ingin membaginya dengan kelas lain, tetapi sudah memiliki kelas dasar karena menggunakan kembali metode lain (atau bahkan, itu bagian dari hierarki "nyata" yang digunakan secara polimorfis (?)). Gunakan pewarisan ketika ClassA adalah jenis ClassB (mis. Mobil yang diwarisi dari Kendaraan), bukan ketika berbagi beberapa detail implementasi internal :-)
Danny Tuppeny
11

Apakah layak mengikuti aturan praktis ini?

Ini adalah aturan praktis yang baik, tetapi saya tahu beberapa tempat di mana saya melanggarnya.

Agar adil, saya menggunakan kelas dasar (abstrak) jauh lebih banyak daripada rekan-rekan saya. Saya melakukan ini sebagai pemrograman defensif.

Bagi saya (dalam banyak bahasa), kelas dasar abstrak menambahkan batasan kunci bahwa mungkin hanya ada satu kelas dasar. Dengan memilih untuk menggunakan kelas dasar daripada antarmuka, Anda mengatakan "ini adalah fungsi inti dari kelas (dan pewaris apa pun), dan tidak pantas untuk menggabungkan mau tidak mau dengan fungsi lainnya". Antarmuka adalah sebaliknya: "Ini adalah beberapa sifat dari suatu objek, belum tentu tanggung jawab intinya".

Pilihan desain semacam ini membuat panduan implisit untuk implementator Anda tentang bagaimana mereka harus menggunakan kode Anda, dan dengan ekstensi menulis kode mereka. Karena dampaknya yang lebih besar pada basis kode, saya cenderung mempertimbangkan hal-hal ini lebih kuat ketika membuat keputusan desain.

Telastyn
sumber
1
Membaca ulang jawaban ini 3 tahun kemudian, saya menemukan ini menjadi poin yang bagus: Dengan memilih untuk menggunakan kelas dasar daripada antarmuka, Anda mengatakan "ini adalah fungsi inti dari kelas (dan pewaris mana pun), dan itu adalah tidak pantas untuk menggabungkan mau tak mau dengan fungsi lain "
sbichenko
9

Masalah dengan penyederhanaan teman Anda adalah menentukan apakah suatu metode perlu diubah atau tidak sangat sulit. Lebih baik menggunakan aturan yang berbicara dengan mentalitas di balik kacamata super dan antarmuka.

Alih-alih mencari tahu apa yang harus Anda lakukan dengan melihat apa yang menurut Anda fungsionalitasnya, lihat hierarki yang Anda coba buat.

Beginilah cara seorang profesor saya mengajarkan perbedaan:

Superclasses harus is adan antarmuka acts likeatau is. Jadi is amamalia dan acts likepemakan anjing . Ini akan memberi tahu kita bahwa mamalia harus kelas dan pemakan harus antarmuka. Contoh isakan The cake is eatableatau The book is writable(membuat keduanya eatabledan writableantarmuka).

Menggunakan metodologi seperti ini cukup sederhana dan mudah, dan menyebabkan Anda membuat kode ke struktur dan konsep daripada apa yang Anda pikir akan dilakukan oleh kode. Ini membuat perawatan lebih mudah, kode lebih mudah dibaca, dan desain lebih mudah untuk dibuat.

Terlepas dari apa kode sebenarnya mungkin mengatakan, jika Anda menggunakan metode seperti ini, Anda bisa kembali lima tahun dari sekarang dan menggunakan metode yang sama untuk menelusuri kembali langkah-langkah Anda dan dengan mudah mengetahui bagaimana program Anda dirancang dan bagaimana ia berinteraksi.

Hanya dua sen saya.

Mirrored Fate
sumber
6

Atas permintaan exizt, saya memperluas komentar saya ke jawaban yang lebih panjang.

Kesalahan terbesar yang dilakukan orang adalah percaya bahwa antarmuka hanyalah kelas abstrak yang kosong. Antarmuka adalah cara bagi seorang programmer untuk mengatakan, "Saya tidak peduli apa yang Anda berikan kepada saya, asalkan mengikuti konvensi ini."

Pustaka .NET berfungsi sebagai contoh yang bagus. Misalnya, ketika Anda menulis fungsi yang menerima IEnumerable<T>, apa yang Anda katakan adalah, "Saya tidak peduli bagaimana Anda menyimpan data Anda. Saya hanya ingin tahu bahwa saya bisa menggunakan foreachloop.

Ini mengarah pada beberapa kode yang sangat fleksibel. Tiba-tiba untuk diintegrasikan, yang perlu Anda lakukan hanyalah bermain sesuai aturan antarmuka yang ada. Jika mengimplementasikan antarmuka sulit atau membingungkan, maka mungkin itu adalah petunjuk bahwa Anda mencoba untuk mendorong pasak persegi di lubang bundar.

Tetapi kemudian muncul pertanyaan: "Bagaimana dengan penggunaan kembali kode? Profesor CS saya mengatakan kepada saya bahwa warisan adalah solusi untuk semua masalah penggunaan kembali kode dan bahwa warisan memungkinkan Anda untuk menulis sekali dan menggunakan di mana-mana dan itu akan menyembuhkan menangitis dalam proses menyelamatkan anak yatim piatu. dari laut yang naik dan tidak akan ada lagi air mata dan seterusnya dll dll. "

Menggunakan pewarisan hanya karena Anda menyukai bunyi kata "penggunaan kembali kode" adalah ide yang sangat buruk. The panduan kode styling oleh Google membuat titik ini cukup ringkas:

Komposisi seringkali lebih tepat daripada warisan. ... [B] karena kode yang mengimplementasikan sub-kelas tersebar antara basis dan sub-kelas, mungkin akan lebih sulit untuk memahami suatu implementasi. Subkelas tidak dapat mengesampingkan fungsi yang bukan virtual, sehingga subkelas tidak dapat mengubah implementasi.

Untuk mengilustrasikan mengapa pewarisan tidak selalu jawabannya, saya akan menggunakan kelas yang disebut MySpecialFileWriter †. Seseorang yang secara buta percaya bahwa pewarisan adalah solusi untuk semua masalah akan berargumen bahwa Anda harus mencoba mewarisi FileStream, jangan sampai Anda menduplikasi FileStreamkode. Orang pintar menyadari bahwa ini bodoh. Anda seharusnya hanya memiliki FileStreamobjek di kelas Anda (baik sebagai variabel lokal atau anggota) dan menggunakan fungsinya.

The FileStreamcontoh mungkin tampak dibikin, tapi tidak. Jika Anda memiliki dua kelas yang keduanya mengimplementasikan antarmuka yang sama dengan cara yang sama persis, maka Anda harus memiliki kelas ketiga yang merangkum operasi apa pun yang diduplikasi. Sasaran Anda adalah menulis kelas-kelas yang berisi blok-blok yang dapat digunakan sendiri dan dapat disatukan seperti lego.

Ini tidak berarti bahwa warisan harus dihindari dengan cara apa pun. Ada banyak hal yang perlu dipertimbangkan, dan sebagian besar akan dibahas dengan meneliti pertanyaan, "Komposisi vs Warisan." Stack Overflow kami sendiri memiliki beberapa jawaban bagus tentang masalah ini.

Pada akhirnya, sentimen rekan kerja Anda tidak memiliki kedalaman atau pemahaman yang diperlukan untuk membuat keputusan. Teliti subjeknya dan cari tahu sendiri.

† Saat menggambarkan warisan, semua orang menggunakan hewan. Itu tidak berguna. Dalam 11 tahun pengembangan, saya tidak pernah menulis sebuah kelas bernama Cat, jadi saya tidak akan menggunakannya sebagai contoh.

riwalk
sumber
Jadi, jika saya mengerti Anda dengan benar, injeksi ketergantungan menyediakan kemampuan penggunaan kembali kode yang sama dengan warisan. Tapi untuk apa Anda menggunakan warisan? Apakah Anda akan menyediakan case-use? (Saya sangat menghargai yang satu dengan FileStream)
sbichenko
1
@ express, tidak itu tidak menyediakan kemampuan penggunaan kembali kode yang sama. Ini memberikan kemampuan penggunaan kembali kode yang lebih baik (dalam banyak kasus) karena membuat implementasi terkandung dengan baik. Kelas-kelas berinteraksi secara eksplisit melalui objek-objek mereka dan API publik mereka, daripada secara implisit mendapatkan kemampuan dan sepenuhnya mengubah setengah dari fungsionalitas dengan membebani fungsi-fungsi yang ada.
riwalk
4

Contoh-contoh jarang membuat titik, terutama ketika mereka tentang mobil atau hewan. Cara makan binatang (atau mengolah makanan) tidak benar-benar terikat pada warisannya, tidak ada cara makan tunggal yang akan berlaku untuk semua hewan. Ini lebih tergantung pada kemampuan fisiknya (cara input, pemrosesan, dan output, demikian dikatakan). Mamalia dapat menjadi karnivora, herbivora atau omnivora misalnya, sementara burung, mamalia dan ikan dapat memakan serangga. Perilaku ini dapat ditangkap dengan pola-pola tertentu.

Anda dalam hal ini lebih baik dengan mengabstraksi perilaku, seperti ini:

public interface IFoodEater
{
    void Eat(IFood food);
}

public class Animal : IFoodEater
{
    private IFoodProcessor _foodProcessor;
    public Animal(IFoodProcessor foodProcessor)
    {
        _foodProcessor = foodProcessor;
    }

    public void Eat(IFood food)
    {
        _foodProcessor.Process(food);
    }
}

Atau menerapkan pola pengunjung tempat FoodProcessormencoba memberi makan hewan yang kompatibel (ICarnivore : IFoodEater , MeatProcessingVisitor). Pikiran tentang binatang hidup dapat menghalangi Anda dalam berpikir untuk merobek saluran pencernaan mereka dan menggantinya dengan yang umum, tetapi Anda benar-benar membantu mereka.

Ini akan membuat hewan Anda menjadi kelas dasar, dan bahkan lebih baik, yang tidak perlu Anda ubah lagi saat menambahkan jenis makanan baru dan prosesor yang sesuai, sehingga Anda dapat fokus pada hal-hal yang membuat kelas hewan khusus ini menjadi bekerja sangat unik.

Jadi ya, kelas dasar memiliki tempat mereka sendiri, aturan praktis berlaku (sering, tidak selalu, karena merupakan aturan praktis) tetapi tetap mencari perilaku yang dapat Anda isolasi.

CodeCaster
sumber
1
IFoodEater agak berlebihan. Apa lagi yang Anda harapkan untuk bisa makan? Ya, hewan sebagai contoh adalah hal yang mengerikan.
Euforia
1
Bagaimana dengan tanaman karnivora? :) Serius, satu masalah utama dengan tidak menggunakan antarmuka dalam kasus ini adalah Anda telah secara erat mengikat konsep makan dengan Hewan dan itu jauh lebih banyak pekerjaan untuk memperkenalkan abstraksi baru yang tidak sesuai dengan kelas dasar Animal.
Julia Hayward
1
Saya percaya "makan" adalah ide yang salah di sini: lebih abstrak. Bagaimana dengan IConsumerantarmuka. Karnivora dapat mengonsumsi daging, herbivora mengonsumsi tanaman, dan tanaman mengonsumsi sinar matahari dan air.
2

Antarmuka benar-benar kontrak. Tidak ada fungsi. Terserah implementor untuk mengimplementasikan. Tidak ada pilihan karena seseorang harus melaksanakan kontrak.

Kelas abstrak memberikan pilihan implementor. Seseorang dapat memilih untuk memiliki metode yang harus diimplementasikan setiap orang, menyediakan metode dengan fungsionalitas dasar yang dapat ditimpa, atau menyediakan fungsionalitas dasar yang dapat digunakan semua pewaris.

Sebagai contoh:

public abstract class Person
    {
        /// <summary>
        /// Inheritors must implement a hello
        /// </summary>
        /// <returns>Hello</returns>
        public abstract string SayHello();
        /// <summary>
        /// Inheritors can use base functionality, or override
        /// </summary>
        /// <returns></returns>
        public virtual string SayGoodBye()
        {
            return "Good Bye";
        }
        /// <summary>
        /// Base Functionality that is inherited 
        /// </summary>
        public void DoSomething()
        {
            //Do Something Here
        }
    }

Saya akan mengatakan aturan praktis Anda dapat ditangani oleh kelas dasar juga. Khususnya karena banyak mamalia mungkin "makan" yang sama kemudian menggunakan kelas dasar mungkin lebih baik daripada antarmuka, karena orang dapat menerapkan metode makan virtual yang dapat digunakan sebagian besar pewaris dan kemudian kasus luar biasa dapat menimpa.

Sekarang jika definisi "makan" sangat bervariasi dari hewan ke hewan maka mungkin dan antarmuka akan lebih baik. Ini terkadang merupakan area abu-abu dan tergantung pada situasi individu.

Jon Raynor
sumber
1

Tidak.

Antarmuka adalah tentang bagaimana metode diimplementasikan. Jika Anda mendasarkan keputusan Anda kapan saja untuk menggunakan antarmuka tentang bagaimana metode akan diimplementasikan, maka Anda melakukan sesuatu yang salah.

Bagi saya, perbedaan antara antarmuka dan kelas dasar adalah tentang di mana persyaratan berdiri. Anda membuat antarmuka, ketika beberapa bagian kode membutuhkan kelas dengan API spesifik dan perilaku tersirat yang muncul dari API ini. Anda tidak dapat membuat antarmuka tanpa kode yang benar-benar akan memanggilnya.

Di sisi lain, kelas dasar biasanya dimaksudkan sebagai cara untuk menggunakan kembali kode, jadi Anda jarang repot dengan kode yang akan memanggil kelas dasar ini dan hanya mempertimbangkan objek-objek hiu yang menjadi bagian dari kelas dasar ini.

Euforia
sumber
Tunggu, mengapa menerapkan kembali eat()pada setiap hewan? Kita bisa mammalmenerapkannya, bukan? Saya mengerti maksud Anda tentang persyaratan.
sbichenko
@ express: Maaf, saya salah membaca pertanyaan Anda.
Euforia
1

Ini sebenarnya bukan aturan praktis yang baik karena jika Anda menginginkan implementasi yang berbeda, Anda selalu dapat memiliki kelas dasar abstrak dengan metode abstrak. Setiap pewaris dijamin memiliki metode itu, tetapi masing-masing mendefinisikan implementasinya sendiri. Secara teknis Anda bisa memiliki kelas abstrak dengan apa-apa selain serangkaian metode abstrak, dan pada dasarnya akan sama dengan antarmuka.

Kelas-kelas dasar berguna ketika Anda menginginkan implementasi aktual dari beberapa keadaan atau perilaku yang dapat digunakan di beberapa kelas. Jika Anda menemukan diri Anda dengan beberapa kelas yang memiliki bidang dan metode yang identik dengan implementasi yang identik, Anda mungkin bisa mengubahnya menjadi kelas dasar. Anda hanya harus berhati-hati dalam bagaimana Anda mewarisi. Setiap bidang atau metode yang ada di kelas dasar harus masuk akal di pewaris, terutama ketika itu dilemparkan sebagai kelas dasar.

Antarmuka berguna ketika Anda perlu berinteraksi dengan satu set kelas dengan cara yang sama, tetapi Anda tidak dapat memprediksi atau tidak peduli dengan implementasi aktualnya. Ambil contoh sesuatu seperti antarmuka Koleksi. Hanya Tuhan yang tahu banyak cara seseorang dapat memutuskan untuk mengimplementasikan koleksi barang. Daftar tertaut? Daftar array? Tumpukan? Antre? Metode SearchAndReplace ini tidak peduli, itu hanya perlu melewati sesuatu yang dapat memanggil Tambah, Hapus, dan GetEnumerator aktif.

pengguna1100269
sumber
1

Saya semacam memperhatikan sendiri jawaban atas pertanyaan ini, untuk melihat apa yang orang lain katakan, tetapi saya akan mengatakan tiga hal yang cenderung saya pikirkan tentang ini:

  1. Orang menaruh terlalu banyak iman ke dalam antarmuka, dan terlalu sedikit ke dalam kelas. Antarmuka pada dasarnya sangat dipermudah kelas dasar, dan ada kalanya menggunakan sesuatu yang ringan dapat menyebabkan hal-hal seperti kode boilerplate yang berlebihan.

  2. Tidak ada yang salah dengan mengganti metode, memanggilnya super.method(), dan membiarkan seluruh tubuhnya melakukan hal-hal yang tidak mengganggu kelas dasar. Ini tidak melanggar Prinsip Pergantian Liskov .

  3. Sebagai aturan praktis, menjadi kaku dan dogmatis dalam rekayasa perangkat lunak adalah ide yang buruk, bahkan jika sesuatu umumnya merupakan praktik terbaik.

Jadi saya akan mengambil apa yang dikatakan dengan sebutir garam.

Panzercrisis
sumber
5
People put way too much faith into interfaces, and too little faith into classes.Lucu. Saya cenderung berpikir sebaliknya.
Simon Bergot
1
"Ini tidak melanggar Prinsip Pergantian Liskov." Tapi itu sangat dekat menjadi pelanggaran terhadap LSP. Lebih baik aman daripada menyesal.
Euforia
1

Saya ingin mengambil pendekatan bahasa dan semantik terhadap hal ini. Antarmuka tidak ada untuk DRY. Meskipun dapat digunakan sebagai mekanisme sentralisasi bersama dengan kelas abstrak yang mengimplementasikannya, itu tidak dimaksudkan untuk KERING.

Antarmuka adalah kontrak.

IAnimalantarmuka adalah kontrak semua-atau-tidak sama sekali antara buaya, kucing, anjing dan gagasan tentang binatang. Apa yang dikatakannya pada dasarnya adalah "jika Anda ingin menjadi binatang, Anda harus memiliki semua atribut dan karakteristik ini, atau Anda bukan binatang lagi".

Warisan di sisi lain adalah mekanisme untuk digunakan kembali. Dalam hal ini, saya pikir memiliki alasan semantik yang baik dapat membantu Anda memutuskan metode mana yang harus digunakan. Misalnya, sementara semua hewan mungkin tidur, dan tidur sama, saya tidak akan menggunakan kelas dasar untuk itu. Saya pertama kali menempatkan Sleep()metode di IAnimalantarmuka sebagai penekanan (semantik) untuk apa yang diperlukan untuk menjadi hewan, kemudian saya menggunakan kelas abstrak dasar untuk kucing, anjing, dan buaya sebagai teknik pemrograman untuk tidak mengulangi diri saya.

Saeed Neamati
sumber
0

Saya tidak yakin ini adalah aturan praktis terbaik di luar sana. Anda bisa meletakkan abstractmetode di kelas dasar dan meminta setiap kelas menerapkannya. Karena setiap orang mammalharus makan, masuk akal untuk melakukannya. Kemudian masing mammal- masing dapat menggunakan satu eatmetodenya. Saya biasanya hanya menggunakan antarmuka untuk komunikasi antara objek, atau sebagai penanda untuk menandai kelas sebagai sesuatu. Saya pikir antarmuka menggunakan terlalu banyak untuk kode ceroboh.

Saya juga setuju dengan @Panzercrisis bahwa aturan praktis seharusnya hanya menjadi pedoman umum. Bukan sesuatu yang harus ditulis dalam batu. Tidak diragukan lagi akan ada saat-saat di mana ia tidak berfungsi.

Jason Crosby
sumber
-1

Antarmuka adalah tipe. Mereka tidak memberikan implementasi. Mereka mendefinisikan kontrak untuk menangani jenis itu. Kontrak ini harus dipenuhi oleh kelas yang mengimplementasikan antarmuka.

Penggunaan antarmuka dan kelas abstrak / dasar harus ditentukan oleh persyaratan desain.

Satu kelas tidak memerlukan antarmuka.

Jika dua kelas dipertukarkan dalam suatu fungsi, mereka harus mengimplementasikan antarmuka umum. Fungsionalitas apa pun yang diimplementasikan secara identik kemudian dapat direactored menjadi kelas abstrak yang mengimplementasikan antarmuka. Antarmuka dapat dianggap berlebihan pada saat itu jika tidak ada fungsi yang membedakan. Tapi apa bedanya kedua kelas itu?

Menggunakan kelas abstrak sebagai tipe umumnya berantakan karena lebih banyak fungsi ditambahkan dan sebagai hierarki diperluas.

Komposisi nikmat daripada warisan - semua ini hilang.

pengguna108620
sumber