Saya hanya merancang aplikasi saya dan saya tidak yakin apakah saya mengerti SOLID dan OOP dengan benar. Kelas harus melakukan 1 hal dan melakukannya dengan baik tetapi dari sisi lain mereka harus mewakili objek nyata yang bekerja dengan kita.
Dalam kasus saya, saya melakukan ekstraksi fitur pada dataset dan kemudian saya melakukan analisis pembelajaran mesin. Saya berasumsi bahwa saya dapat membuat tiga kelas
- FeatureExtractor
- Set Data
- Penganalisa
Tetapi kelas FeatureExtractor tidak mewakili apa pun, ia melakukan sesuatu yang membuatnya lebih sebagai rutinitas daripada kelas. Ini hanya memiliki satu fungsi yang akan digunakan: extract_features ()
Apakah benar membuat kelas yang tidak mewakili satu hal tetapi melakukan satu hal?
EDIT: tidak yakin apakah itu penting tapi saya menggunakan Python
Dan jika extract_features () akan terlihat seperti itu: apakah layak membuat kelas khusus untuk menampung metode itu?
def extract_features(df):
extr = PhrasesExtractor()
extr.build_vocabulary(df["Text"].tolist())
sent = SentimentAnalyser()
sent.load()
df = add_features(df, extr.features)
df = mark_features(df, extr.extract_features)
df = drop_infrequent_features(df)
df = another_processing1(df)
df = another_processing2(df)
df = another_processing3(df)
df = set_sentiment(df, sent.get_sentiment)
return df
sumber
Jawaban:
Ya, itu umumnya pendekatan yang baik.
Tidak, itu adalah kesalahpahaman umum IMHO. Akses pemula yang baik ke OOP sering "mulai dengan objek yang mewakili hal-hal dari dunia nyata" , itu benar.
Namun, Anda tidak boleh berhenti dengan ini !
Kelas dapat (dan harus) digunakan untuk menyusun program Anda dengan berbagai cara. Membuat model objek dari dunia nyata adalah salah satu aspek dari ini, tetapi bukan satu-satunya. Membuat modul atau komponen untuk tugas tertentu adalah kasus penggunaan lain yang masuk akal untuk kelas. "Ekstraktor fitur" mungkin merupakan modul seperti itu, dan meskipun hanya berisi satu metode publik
extract_features()
, saya akan heran jika jika tidak juga mengandung banyak metode pribadi dan mungkin beberapa keadaan bersama. Jadi memiliki kelasFeatureExtractor
akan memperkenalkan lokasi alami untuk metode pribadi ini.Catatan: dalam bahasa seperti Python yang mendukung konsep modul terpisah, seseorang juga dapat menggunakan modul
FeatureExtractor
untuk ini, tetapi dalam konteks pertanyaan ini, ini adalah IMHO perbedaan yang dapat diabaikan.Selain itu, "ekstraktor fitur" dapat dibayangkan sebagai "orang atau bot yang mengekstraksi fitur". Itu adalah "benda" abstrak, mungkin bukan benda yang akan Anda temukan di dunia nyata, tetapi nama itu sendiri adalah abstraksi yang berguna, yang memberi setiap orang gagasan tentang apa tanggung jawab kelas itu. Jadi saya tidak setuju bahwa kelas ini tidak "mewakili apa pun".
sumber
extract_features
? Saya agak berasumsi bahwa itu adalah acara publik dari tempat lain. Cukup adil, saya setuju bahwa jika ini pribadi maka mereka mungkin harus masuk ke modul (tapi tetap: bukan kelas, kecuali mereka berbagi negara), bersama denganextract_features
. (Konon, Anda tentu saja dapat mendeklarasikannya secara lokal di dalam fungsi itu.)Doc Brown tepat: kelas tidak perlu mewakili objek dunia nyata. Mereka hanya perlu bermanfaat . Kelas adalah jenis dasarnya hanya tambahan, dan apa yang
int
ataustring
sesuai dengan di dunia nyata? Mereka adalah deskripsi abstrak, bukan hal-hal konkret, nyata.Yang mengatakan, kasing Anda istimewa. Menurut uraian Anda:
Anda benar sekali: jika kode Anda seperti yang ditunjukkan, tidak ada gunanya menjadikannya sebuah kelas. Ada pembicaraan terkenal yang berpendapat bahwa penggunaan kelas-kelas semacam itu di Python adalah bau kode, dan bahwa fungsi-fungsi sederhana seringkali cukup. Kasing Anda adalah contoh sempurna dari ini.
Terlalu sering menggunakan kelas disebabkan oleh kenyataan bahwa OOP menjadi arus utama dengan Jawa pada 1990-an. Sayangnya Java pada saat itu tidak memiliki beberapa fitur bahasa modern (seperti penutupan), yang berarti bahwa banyak konsep sulit atau tidak mungkin untuk diungkapkan tanpa menggunakan kelas. Misalnya, tidak mungkin di Jawa sampai saat ini untuk memiliki metode yang dilakukan negara (yaitu penutupan). Sebagai gantinya, Anda harus menulis kelas untuk membawa negara, dan yang memaparkan metode tunggal (disebut sesuatu seperti
invoke
).Sayangnya gaya pemrograman ini menjadi populer jauh di luar Jawa (sebagian karena buku rekayasa perangkat lunak berpengaruh yang sebaliknya sangat berguna), bahkan dalam bahasa yang tidak memerlukan solusi seperti itu.
Dalam Python, kelas jelas merupakan alat yang sangat penting dan harus digunakan secara bebas. Tapi itu bukan satu - satunya alat, dan tidak ada alasan untuk menggunakannya di tempat yang tidak masuk akal. Ini adalah kesalahpahaman umum bahwa fungsi bebas tidak memiliki tempat di OOP.
sumber
__call__
ditentukan, dalam hal ini gunakan saja fungsi!)__call__()
? Apakah itu benar-benar berbeda dari instance kelas batin anonim? Secara sintaksis, pasti tetapi dari desain bahasa, sepertinya perbedaan yang kurang signifikan dari yang Anda sajikan di sini.Sudah lebih dari 20 tahun dan saya tidak yakin juga.
Sulit salah di sini.
Oh benarkah? Izinkan saya memperkenalkan Anda ke kelas yang paling sukses populer dan sepanjang masa:
String
. Kami menggunakannya untuk teks. Dan objek dunia nyata yang diwakilinya adalah ini:Kenapa tidak, tidak semua programmer terobsesi dengan memancing. Di sini kita menggunakan sesuatu yang disebut metafora. Boleh jadi membuat model hal-hal yang tidak benar-benar ada. Itu ide yang harus jelas. Anda sedang membuat gambar di benak pembaca Anda. Gambar-gambar itu tidak harus nyata. Mudah dipahami.
Desain OOP yang baik mengelompokkan pesan (metode) di sekitar data (keadaan) sehingga reaksi terhadap pesan tersebut dapat bervariasi tergantung pada data tersebut. Jika melakukan itu memodelkan beberapa hal dunia nyata, keren. Jika tidak, baiklah. Selama itu masuk akal bagi pembaca, tidak apa-apa.
Sekarang yakin, Anda bisa memikirkannya seperti ini:
tetapi jika Anda berpikir ini harus ada di dunia nyata sebelum Anda dapat menggunakan metafora, baik karir pemrograman Anda akan melibatkan banyak seni dan kerajinan.
sumber
class
dantable
dancolumn
...Waspadalah! Tidak ada kata SOLID yang mengatakan kelas hanya "melakukan satu hal". Jika itu masalahnya, kelas hanya akan memiliki metode tunggal, dan tidak akan ada perbedaan nyata antara kelas dan fungsi.
SOLID mengatakan kelas harus mewakili satu tanggung jawab . Ini adalah semacam tanggung jawab orang dalam sebuah tim: Pengemudi, pengacara, pencopet, perancang grafis dll. Masing-masing orang ini dapat melakukan banyak tugas (terkait), tetapi semuanya berkaitan dengan satu tanggung jawab.
Intinya adalah - jika ada perubahan dalam persyaratan, idealnya Anda hanya perlu memodifikasi satu kelas. Ini hanya membuat kode lebih mudah dipahami, lebih mudah untuk memodifikasi dan mengurangi risiko.
Tidak ada aturan bahwa suatu benda harus mewakili "hal yang nyata". Ini hanya pengetahuan kargo karena OO awalnya diciptakan untuk digunakan dalam simulasi. Tetapi program Anda bukan simulasi (beberapa aplikasi OO modern), jadi aturan ini tidak berlaku. Selama setiap kelas memiliki tanggung jawab yang jelas, Anda harus baik-baik saja.
Jika suatu kelas benar-benar hanya memiliki metode tunggal dan kelas tidak memiliki keadaan apa pun, Anda dapat mempertimbangkan menjadikannya fungsi yang berdiri sendiri. Ini baik-baik saja dan mengikuti prinsip KISS dan YAGNI - tidak perlu membuat kelas jika Anda dapat menyelesaikannya dengan suatu fungsi. Di sisi lain, jika Anda memiliki alasan untuk percaya bahwa Anda mungkin memerlukan kondisi internal atau implementasi ganda, Anda mungkin juga menjadikannya kelas di muka. Anda harus menggunakan penilaian terbaik Anda di sini.
sumber
Secara umum itu tidak masalah.
Tanpa sedikit deskripsi yang lebih spesifik apa yang
FeatureExtractor
seharusnya dilakukan kelas sebenarnya sulit untuk dikatakan.Bagaimanapun, bahkan jika
FeatureExtractor
mengekspos hanyaextract_features()
fungsi publik , saya bisa memikirkan mengonfigurasinya denganStrategy
kelas, yang menentukan bagaimana tepatnya ekstraksi harus dilakukan.Contoh lain adalah kelas dengan fungsi Templat .
Dan ada lebih banyak Pola Desain Perilaku , yang didasarkan pada model kelas.
Ketika Anda menambahkan beberapa kode untuk klarifikasi.
Garis
persis terdiri dari apa yang saya maksudkan bahwa Anda dapat mengkonfigurasi kelas dengan Strategi .
Jika Anda memiliki antarmuka untuk
SentimentAnalyser
kelas itu, Anda bisa meneruskannya keFeatureExtractor
kelas pada titik konstruksi, alih-alih langsung menyambungkan ke implementasi spesifik dalam fungsi Anda.sumber
FeatureExtractor
kelas) hanya untuk memperkenalkan kompleksitas yang lebih (antarmuka untukSentimentAnalyser
kelas). Jika decoupling diinginkan, makaextract_features
dapat mengambilget_sentiment
fungsi sebagai argumen (load
panggilan tampaknya independen dari fungsi dan hanya dipanggil untuk pengaruhnya). Perhatikan juga bahwa Python tidak memiliki / mendorong antarmuka.CacheingFeatureExtractor
atau aTimeSeriesDependentFeatureExtractor
) maka suatu objek akan jauh lebih cocok. Hanya karena tidak perlu untuk objek saat ini tidak berarti tidak akan pernah ada.__call__
Metode akan kompatibel jika Anda membutuhkannya (Anda tidak akan), keempat dengan menambahkan pembungkus sepertiFeatureExtractor
Anda membuat kode tidak kompatibel dengan semua kode lain yang pernah ditulis (kecuali jika Anda memberikan__call__
metode, dalam hal ini fungsi akan jelas lebih sederhana )Pola dan semua bahasa mewah / konsep samping: apa yang Anda temui adalah Pekerjaan atau Proses Batch .
Pada akhirnya, bahkan sebuah program OOP murni perlu entah bagaimana didorong oleh sesuatu, untuk benar-benar melakukan pekerjaan; pasti ada titik masuknya. Dalam pola MVC, misalnya, ontroller "C" menerima peristiwa klik dll dari GUI dan kemudian mengatur komponen lainnya. Dalam alat baris perintah klasik, fungsi "utama" akan melakukan hal yang sama.
Kelas Anda mewakili entitas yang melakukan sesuatu dan mengatur segalanya. Anda dapat menamainya Pengendali , Pekerjaan , Utama atau apa pun yang terlintas dalam pikiran.
Itu tergantung pada keadaan (dan saya tidak terbiasa dengan cara biasa ini dilakukan dengan Python). Jika ini hanyalah alat baris perintah satu-shot kecil, maka metode alih-alih kelas harus baik-baik saja. Versi pertama dari program Anda pasti bisa lolos dengan metode. Jika, kemudian, Anda mendapati bahwa Anda berakhir dengan puluhan metode seperti itu, mungkin bahkan dengan variabel global yang tercampur, maka inilah saatnya untuk refactor ke dalam kelas.
sumber
this
atau eksplisitself
) dari metode tersebut. Metode objek saling "terbuka" secara rekursif, jadi menggantifoo
penyebab semuaself.foo
panggilan menggunakan penggantian ini.Kita dapat menganggap OOP sebagai pemodelan perilaku suatu sistem. Perhatikan bahwa sistem tidak harus ada di 'dunia nyata', meskipun metafora dunia nyata kadang-kadang bisa berguna (misalnya "jaringan pipa", "pabrik", dll.).
Jika sistem yang kita inginkan terlalu rumit untuk memodelkan sekaligus, kita dapat memecahnya menjadi potongan-potongan kecil dan memodelkan mereka ("domain masalah"), yang mungkin melibatkan penguraian lebih lanjut, dan seterusnya sampai kita menemukan bagian-bagian yang perilakunya cocok (kurang lebih) dari beberapa objek bahasa bawaan seperti angka, string, daftar, dll.
Setelah kita memiliki potongan-potongan sederhana itu, kita dapat menggabungkannya untuk menggambarkan perilaku potongan yang lebih besar, yang dapat kita gabungkan menjadi bagian yang lebih besar, dan seterusnya hingga kita dapat menggambarkan semua komponen domain yang diperlukan untuk keseluruhan sistem.
Ini adalah fase "menggabungkan bersama" di mana kita dapat menulis beberapa kelas. Kami menulis kelas ketika tidak ada objek yang ada yang berperilaku seperti yang kita inginkan. Misalnya, domain kami mungkin berisi "foo", koleksi foo yang disebut "bar", dan koleksi bar yang disebut "bazs". Kami mungkin memperhatikan bahwa foo cukup sederhana untuk dimodelkan dengan string, jadi kami melakukannya. Kami menemukan bahwa bilah memerlukan isinya untuk mematuhi batasan tertentu yang tidak cocok dengan apa pun yang disediakan oleh Python, dalam hal ini kami mungkin menulis kelas baru untuk menegakkan batasan ini. Mungkin baz tidak memiliki kekhasan seperti itu, jadi kita bisa mewakili mereka dengan daftar.
Perhatikan bahwa kita dapat menulis kelas baru untuk setiap komponen itu (foos, bar dan bazs), tetapi kita tidak perlu jika sudah ada sesuatu dengan perilaku yang benar. Secara khusus, agar suatu kelas berguna, ia perlu 'menyediakan' sesuatu (data, metode, konstanta, subkelas, dll.), Jadi bahkan jika kita memiliki banyak lapisan kelas khusus kita pada akhirnya harus menggunakan beberapa fitur bawaan; misalnya, jika kita menulis kelas baru untuk foos mungkin hanya berisi string, jadi mengapa tidak melupakan kelas foo dan minta kelas bar berisi string itu? Perlu diingat bahwa kelas juga merupakan objek bawaan, mereka hanya sangat fleksibel.
Setelah kita memiliki model domain kita, kita dapat mengambil beberapa contoh potongan-potongan itu dan mengaturnya menjadi "simulasi" dari sistem tertentu yang ingin kita model (misalnya "sistem pembelajaran mesin untuk ...").
Setelah kita memiliki simulasi ini, kita dapat menjalankannya dan hei presto, kita memiliki kerja (simulasi a) sistem pembelajaran mesin untuk ... (atau apa pun yang kita pemodelan).
Sekarang, dalam situasi khusus Anda, Anda mencoba memodelkan perilaku komponen "extractor fitur". Pertanyaannya adalah, adakah objek bawaan yang berperilaku seperti "ekstraktor fitur", atau akankah Anda perlu memecahnya menjadi hal-hal yang lebih sederhana? Sepertinya ekstraktor fitur berperilaku sangat mirip dengan objek fungsi, jadi saya pikir Anda akan baik-baik saja menggunakannya sebagai model Anda.
Satu hal yang perlu diingat ketika mempelajari konsep-konsep semacam ini adalah bahwa bahasa yang berbeda dapat menyediakan fitur dan objek bawaan yang berbeda (dan, tentu saja, beberapa bahkan tidak menggunakan terminologi seperti "objek"!). Oleh karena itu solusi yang masuk akal dalam satu bahasa mungkin kurang bermanfaat dalam bahasa lain (ini bahkan dapat berlaku untuk versi berbeda dari bahasa yang sama!).
Secara historis, banyak literatur OOP (terutama "pola desain") telah berfokus pada Jawa, yang sangat berbeda dari Python. Sebagai contoh, kelas Java bukan objek, Java tidak memiliki objek fungsi sampai saat ini, Java memiliki pemeriksaan tipe yang ketat (yang mendorong antarmuka dan subkelas) sementara Python mendorong bebek-mengetik, Java tidak memiliki objek modul, Java integers / mengapung / dll. bukan objek, meta-pemrograman / introspeksi di Jawa membutuhkan "refleksi", dan sebagainya.
Saya tidak mencoba memilih di Jawa (sebagai contoh lain, banyak teori OOP berputar di sekitar Smalltalk, yang sekali lagi sangat berbeda dari Python), saya hanya mencoba menunjukkan bahwa kita harus berpikir dengan sangat hati-hati tentang konteks dan kendala di mana solusi dikembangkan, dan apakah itu cocok dengan situasi kita sekarang.
Dalam kasus Anda, objek fungsi sepertinya merupakan pilihan yang baik. Jika Anda bertanya-tanya mengapa beberapa pedoman "praktik terbaik" tidak menyebutkan objek fungsi sebagai solusi yang mungkin, itu mungkin saja karena pedoman itu ditulis untuk versi Jawa yang lama!
sumber
Secara pragmatis, ketika saya memiliki "hal lain yang melakukan sesuatu yang penting dan harus dipisahkan", dan tidak memiliki rumah yang jelas, saya meletakkannya di
Utilities
bagian dan menggunakannya sebagai konvensi penamaan saya. yaitu.FeatureExtractionUtility
.Lupakan jumlah metode di kelas; satu metode hari ini mungkin perlu tumbuh menjadi lima metode besok. Yang penting adalah struktur organisasi yang jelas dan konsisten, seperti area utilitas untuk koleksi fungsi lainnya.
sumber