Perhatikan contoh di bawah ini. Setiap perubahan pada enum ColorChoice mempengaruhi semua subkelas IWindowColor.
Apakah enum cenderung menyebabkan antarmuka yang rapuh? Adakah sesuatu yang lebih baik daripada enum untuk memungkinkan fleksibilitas polimorfik yang lebih banyak?
enum class ColorChoice
{
Blue = 0,
Red = 1
};
class IWindowColor
{
public:
ColorChoice getColor() const=0;
void setColor( const ColorChoice value )=0;
};
Sunting: maaf karena menggunakan warna sebagai contoh saya, bukan itu pertanyaannya. Berikut adalah contoh berbeda yang menghindari herring merah dan memberikan lebih banyak informasi tentang apa yang saya maksud dengan fleksibilitas.
enum class CharacterType
{
orc = 0,
elf = 1
};
class ISomethingThatNeedsToKnowWhatTypeOfCharacter
{
public:
CharacterType getCharacterType() const;
void setCharacterType( const CharacterType value );
};
Lebih jauh, bayangkan bahwa menangani ke subclass ISomethingThatNeedsToKnowWhatTypeOfCharacter yang tepat diberikan oleh pola desain pabrik. Sekarang saya memiliki API yang tidak dapat diperpanjang di masa depan untuk aplikasi yang berbeda di mana tipe karakter yang diijinkan adalah {human, dwarf}.
Sunting: Hanya untuk lebih konkret tentang apa yang saya kerjakan. Saya merancang pengikatan yang kuat dari spesifikasi ( MusicXML ) ini dan saya menggunakan kelas enum untuk mewakili tipe-tipe tersebut dalam spesifikasi yang dideklarasikan dengan xs: enumeration. Saya mencoba memikirkan apa yang terjadi ketika versi berikutnya (4.0) keluar. Bisakah perpustakaan kelas saya berfungsi dalam mode 3.0 dan mode 4.0? Jika versi berikutnya kompatibel 100% ke belakang, maka mungkin. Tetapi jika nilai pencacahan dihapus dari spesifikasi maka saya mati di air.
sumber
Jawaban:
Ketika digunakan dengan benar, enum jauh lebih mudah dibaca dan kuat daripada "angka ajaib" yang mereka ganti. Saya biasanya tidak melihat mereka membuat kode lebih rapuh. Contohnya:
value
ini merupakan nilai warna yang valid atau tidak. Kompiler sudah melakukan itu.enum class
fitur di C ++ modern bahkan memungkinkan Anda memaksa orang untuk selalu menulis yang pertama, bukan yang terakhir.Namun, menggunakan enum untuk warna dipertanyakan karena dalam banyak situasi (sebagian besar?) Tidak ada alasan untuk membatasi pengguna untuk sekumpulan kecil warna; Anda mungkin juga membiarkan mereka lulus dalam nilai RGB sewenang-wenang. Pada proyek yang saya kerjakan, daftar kecil warna seperti ini hanya akan muncul sebagai bagian dari serangkaian "tema" atau "gaya" yang seharusnya bertindak sebagai abstraksi tipis atas warna beton.
Saya tidak yakin apa pertanyaan "fleksibilitas polimorfik" Anda. Enum tidak memiliki kode yang dapat dieksekusi, jadi tidak ada yang membuat polimorfik. Mungkin Anda sedang mencari pola perintah ?
Sunting: Pasca-sunting, saya masih tidak jelas pada jenis ekstensi yang Anda cari, tetapi saya masih berpikir pola perintah adalah hal yang paling dekat dengan "polimorfik enum".
sumber
Tidak, tidak. Ada dua kasus: pelaksana juga akan
menyimpan, mengembalikan dan meneruskan nilai enum, tidak pernah beroperasi pada mereka, dalam hal ini mereka tidak terpengaruh oleh perubahan enum, atau
beroperasi pada nilai enum individu, dalam hal ini setiap perubahan dalam enum tentu saja, tentu saja, tak terhindarkan, tentu saja , harus dipertanggungjawabkan dengan perubahan yang sesuai dalam logika pelaksana.
Jika Anda meletakkan "Sphere", "Rectangle", dan "Pyramid" dalam enum "Shape", dan meneruskan enum tersebut ke beberapa
drawSolid()
fungsi yang telah Anda tulis untuk menggambar padatan yang sesuai, dan kemudian suatu pagi Anda memutuskan untuk menambahkan " Nilai Ikositetrahedron ke enum, Anda tidak dapat mengharapkandrawSolid()
fungsi tetap tidak terpengaruh; Jika Anda memang berharap bisa menggambar icositetrahedron tanpa Anda harus terlebih dahulu menulis kode aktual untuk menggambar icositetrahedron, itu salah Anda, bukan kesalahan enum. Begitu:Tidak, mereka tidak. Apa yang menyebabkan antarmuka yang rapuh adalah programmer menganggap diri mereka sebagai ninja, mencoba untuk mengkompilasi kode mereka tanpa mengaktifkan peringatan yang memadai. Kemudian, kompiler tidak memperingatkan mereka bahwa
drawSolid()
fungsinya berisiswitch
pernyataan yang tidak memilikicase
klausa untuk nilai enum "Ikositetrahedron" yang baru ditambahkan.Cara yang seharusnya bekerja adalah analog dengan menambahkan metode virtual murni baru ke kelas dasar: Anda kemudian harus menerapkan metode ini pada setiap pewaris tunggal, atau proyek tidak, dan seharusnya tidak, membangun.
Sekarang, sejujurnya, enum bukanlah konstruksi berorientasi objek. Mereka lebih merupakan kompromi pragmatis antara paradigma berorientasi objek dan paradigma pemrograman terstruktur.
Cara berorientasi objek murni dalam melakukan sesuatu adalah tidak memiliki enum sama sekali, dan sebaliknya memiliki objek sepanjang jalan.
Jadi, cara berorientasi objek murni untuk menerapkan contoh dengan padatan tentu saja menggunakan polimorfisme: alih-alih memiliki metode terpusat mengerikan tunggal yang tahu cara menggambar semuanya dan harus diberitahu yang solid untuk menggambar, Anda menyatakan " Solid "kelas dengan metode abstrak (virtual murni)
draw()
, dan kemudian Anda menambahkan" Sphere "," Rectangle "dan" Pyramid "subclass, masing-masing memiliki implementasi sendiridraw()
yang tahu cara menggambar itu sendiri.Dengan cara ini, ketika Anda memperkenalkan subclass "Ikositetrahedron", Anda hanya perlu menyediakan
draw()
fungsi untuk itu, dan kompiler akan mengingatkan Anda untuk melakukan itu, dengan tidak membiarkan Anda membuat instantiate "Icositetrahedron" sebaliknya.sumber
default:
klausa harus melakukannya. Pencarian cepat pada subjek tidak menghasilkan hasil yang lebih pasti, jadi ini mungkin cocok sebagai subjekprogrammers SE
pertanyaan lain .Pyramid
benar - benar tahu caradraw()
piramida. Paling-paling itu mungkin berasal dariSolid
dan memilikiGetTriangles()
metode dan Anda bisa meneruskannya keSolidDrawer
layanan. Saya pikir kami sudah jauh dari contoh benda fisik sebagai contoh benda di OOP.Enums tidak membuat antarmuka yang getas. Penyalahgunaan enums tidak.
Untuk apa enum?
Enum dirancang untuk digunakan sebagai set konstanta bermakna bernama. Mereka harus digunakan ketika:
Penggunaan enum yang baik:
System.DayOfWeek
) Kecuali Anda berurusan dengan kalender yang sangat tidak jelas, hanya akan ada 7 hari dalam seminggu.System.ConsoleColor
) Beberapa mungkin tidak setuju dengan ini, tetapi. Net memilih untuk melakukan ini karena suatu alasan. Dalam sistem konsol .Net, hanya ada 16 warna yang tersedia untuk digunakan oleh konsol. Ke 16 warna ini sesuai dengan palet warna lawas yang dikenal sebagai CGA atau 'Color Graphics Adapter' . Tidak ada nilai baru yang akan ditambahkan, yang berarti bahwa ini sebenarnya merupakan aplikasi enum yang masuk akal.Thread.State
) Para desainer Jawa memutuskan bahwa dalam model threading Java hanya akan ada satu set tetap menyatakan bahwa aThread
dapat di, dan untuk menyederhanakan masalah, negara-negara yang berbeda ini direpresentasikan sebagai enumerasi . Ini berarti bahwa banyak pemeriksaan berbasis negara adalah ifs dan switch sederhana yang dalam praktiknya beroperasi pada nilai integer, tanpa programmer harus khawatir tentang apa nilai sebenarnya.System.Text.RegularExpressions.RegexOptions
) Bitflags adalah penggunaan enum yang sangat umum. Begitu umum pada kenyataannya, bahwa dalam. Net semua enum memilikiHasFlag(Enum flag)
metode bawaan. Mereka juga mendukung operator bitwise dan adaFlagsAttribute
untuk menandai enum yang dimaksudkan untuk digunakan sebagai satu set bitflag. Dengan menggunakan enum sebagai set flag, Anda dapat mewakili sekelompok nilai boolean dalam nilai tunggal, serta memiliki flag yang diberi nama yang jelas untuk kenyamanan. Ini akan sangat bermanfaat untuk mewakili bendera register status dalam emulator atau untuk mewakili izin file (baca, tulis, eksekusi), atau situasi apa pun di mana serangkaian opsi terkait tidak saling eksklusif.Penggunaan enum yang buruk:
True
bendera tersebut menyiratkanFalse
). Perilaku ini selanjutnya dapat didukung melalui penggunaan fungsi khusus (yaituIsTrue(flags)
danIsFalse(flags)
).sumber
RegexOptions
msdn.microsoft.com/en-us/library/…Enum adalah peningkatan besar dibandingkan angka identifikasi ajaib untuk set nilai tertutup yang tidak memiliki banyak fungsi yang terkait dengannya. Biasanya Anda tidak peduli tentang angka apa yang sebenarnya terkait dengan enum; dalam hal ini mudah untuk diperluas dengan menambahkan entri baru pada akhirnya, tidak ada kerapuhan yang terjadi.
Masalahnya adalah ketika Anda memiliki fungsi signifikan yang terkait dengan enum. Artinya, Anda memiliki kode seperti ini:
Satu atau dua di antaranya tidak masalah, tetapi segera setelah Anda memiliki enum yang bermakna seperti ini,
switch
pernyataan ini cenderung berlipat ganda seperti tribbles. Sekarang setiap kali Anda memperpanjang enum, Anda perlu memburu semua yang terkaitswitch
untuk memastikan Anda telah menutupi semuanya. Ini rapuh. Sumber seperti Clean Code akan menyarankan bahwa Anda harus memiliki paling banyak satuswitch
per enum.Yang harus Anda lakukan sebagai gantinya dalam hal ini adalah menggunakan prinsip-prinsip OO dan membuat antarmuka untuk jenis ini. Anda mungkin masih menyimpan enum untuk mengkomunikasikan tipe ini, tetapi segera setelah Anda perlu melakukan sesuatu dengannya, Anda membuat objek yang terkait dengan enum, mungkin menggunakan Pabrik. Ini jauh lebih mudah untuk dipertahankan, karena Anda hanya perlu mencari satu tempat untuk memperbarui: Pabrik Anda, dan dengan menambahkan kelas baru.
sumber
Jika Anda berhati-hati dalam menggunakannya, saya tidak akan menganggap enum berbahaya. Tetapi ada beberapa hal yang perlu dipertimbangkan jika Anda ingin menggunakannya dalam beberapa kode pustaka, sebagai lawan dari satu aplikasi.
Jangan pernah menghapus atau menyusun ulang nilai. Jika Anda pada beberapa titik memiliki nilai enum dalam daftar Anda, nilai itu harus dikaitkan dengan nama itu untuk selamanya. Jika Anda mau, pada suatu titik Anda dapat mengubah nama nilai menjadi
deprecated_orc
atau apa pun, tetapi dengan tidak menghapusnya, Anda dapat lebih mudah mempertahankan kompatibilitas dengan kode lama yang dikompilasi terhadap set lama enum. Jika beberapa kode baru tidak dapat berurusan dengan konstanta enum lama, hasilkan kesalahan yang sesuai di sana atau pastikan tidak ada nilai yang mencapai potongan kode itu.Jangan lakukan aritmatika pada mereka. Secara khusus, jangan melakukan perbandingan pesanan. Mengapa? Karena dengan begitu Anda mungkin menghadapi situasi di mana Anda tidak dapat mempertahankan nilai-nilai yang ada dan mempertahankan pemesanan yang waras pada saat yang sama. Ambil contoh enum untuk arah kompas: N = 0, NE = 1, E = 2, SE = 3, ... Sekarang jika setelah beberapa pembaruan Anda memasukkan NNE dan seterusnya di antara arah yang ada, Anda dapat menambahkannya ke akhir daftar dan dengan demikian memecah urutan, atau Anda interleave mereka dengan kunci yang ada dan dengan demikian memecah pemetaan yang digunakan yang digunakan dalam kode sebelumnya. Atau Anda mencabut semua kunci lama, dan memiliki satu set kunci yang benar-benar baru, bersama dengan beberapa kode kompatibilitas yang menerjemahkan antara kunci lama dan baru demi kode lama.
Pilih ukuran yang memadai. Secara default kompiler akan menggunakan integer terkecil yang dapat berisi semua nilai enum. Yang berarti bahwa jika, pada beberapa pembaruan, set enum Anda yang mungkin bertambah dari 254 menjadi 259, Anda tiba-tiba membutuhkan 2 byte untuk setiap nilai enum alih-alih satu. Ini dapat merusak struktur dan tata letak kelas di semua tempat, jadi cobalah untuk menghindari ini dengan menggunakan ukuran yang cukup dalam desain pertama. C ++ 11 memberi Anda banyak kontrol di sini, tetapi jika tidak menentukan entri
LAST_SPECIES_VALUE=65535
akan membantu juga.Punya registry pusat. Karena Anda ingin memperbaiki pemetaan antara nama dan nilai, itu buruk jika Anda membiarkan pengguna pihak ketiga dari kode Anda menambahkan konstanta baru. Tidak boleh ada proyek yang menggunakan kode Anda untuk mengubah tajuk itu untuk menambahkan pemetaan baru. Alih-alih, mereka seharusnya mengganggu Anda untuk menambahkannya. Ini berarti bahwa untuk contoh manusia dan kurcaci yang Anda sebutkan, enum memang tidak cocok. Di sana akan lebih baik untuk memiliki semacam registry pada saat run-time, di mana pengguna kode Anda dapat memasukkan string dan mendapatkan kembali nomor unik, yang dibungkus dengan baik dalam beberapa jenis buram. "Angka" bahkan mungkin menjadi pointer ke string yang dimaksud, itu tidak masalah.
Terlepas dari kenyataan bahwa poin terakhir saya di atas membuat enum tidak cocok untuk situasi hipotetis Anda, situasi aktual Anda di mana beberapa spek mungkin berubah dan Anda harus memperbarui beberapa kode tampaknya cocok dengan registri pusat untuk pustaka Anda. Jadi enum harus sesuai di sana jika Anda mempertimbangkan saran saya yang lain.
sumber