Ada banyak alasan mengapa global jahat di OOP.
Jika jumlah atau ukuran objek yang perlu dibagi terlalu besar untuk secara efisien diteruskan dalam parameter fungsi, biasanya semua orang merekomendasikan Dependency Injection daripada objek global.
Namun, dalam kasus di mana hampir semua orang perlu tahu tentang struktur data tertentu, mengapa Ketergantungan Injeksi lebih baik daripada objek global?
Contoh (yang disederhanakan, untuk menunjukkan titik secara umum, tanpa menggali terlalu dalam pada aplikasi tertentu)
Ada sejumlah kendaraan virtual yang memiliki sejumlah besar properti dan status, dari jenis, nama, warna, hingga kecepatan, posisi, dll. Sejumlah pengguna dapat mengendalikan mereka secara jarak jauh, dan sejumlah besar peristiwa (baik pengguna- dimulai dan otomatis) dapat mengubah banyak status atau properti mereka.
Solusi naif adalah dengan hanya membuat wadah global mereka, seperti
vector<Vehicle> vehicles;
yang dapat diakses dari mana saja.
Solusi yang lebih ramah-OOP adalah membuat wadah menjadi anggota kelas yang menangani loop peristiwa utama, dan digunakan di konstruktornya. Setiap kelas yang membutuhkannya, dan anggota dari utas utama, akan diberikan akses ke wadah melalui pointer di konstruktor mereka. Misalnya, jika pesan eksternal masuk melalui koneksi jaringan, kelas (satu untuk setiap koneksi) yang menangani parsing akan mengambil alih, dan parser akan memiliki akses ke wadah melalui pointer atau referensi. Sekarang jika pesan yang diuraikan menghasilkan perubahan pada elemen wadah, atau mengharuskan beberapa data keluar untuk melakukan suatu tindakan, itu dapat ditangani tanpa perlu melemparkan sekitar ribuan variabel melalui sinyal dan slot (atau lebih buruk, menyimpannya di parser untuk kemudian diambil oleh orang yang disebut parser). Tentu saja, semua kelas yang menerima akses ke wadah melalui injeksi ketergantungan, adalah bagian dari utas yang sama. Utas yang berbeda tidak akan langsung mengaksesnya, tetapi melakukan tugasnya dan kemudian mengirim sinyal ke utas utama, dan slot di utas utama akan memperbarui wadah.
Namun, jika sebagian besar kelas akan mendapatkan akses ke wadah, apa yang membuatnya sangat berbeda dari global? Jika begitu banyak kelas membutuhkan data dalam wadah, bukankah "cara injeksi ketergantungan" hanya global yang disamarkan?
Satu jawaban adalah keamanan utas: meskipun saya berhati-hati untuk tidak menyalahgunakan wadah global, mungkin pengembang lain di masa depan, di bawah tekanan tenggat waktu yang dekat, namun akan menggunakan wadah global dalam utas yang berbeda, tanpa mengurus semua kasus tabrakan. Namun, bahkan dalam kasus injeksi dependensi, seseorang dapat memberikan pointer kepada seseorang yang menjalankan thread lain, yang mengarah ke masalah yang sama.
Jawaban:
Ketergantungan injeksi adalah hal terbaik sejak mengiris roti , sementara objek global telah dikenal selama beberapa dekade sebagai sumber dari semua kejahatan , jadi ini adalah pertanyaan yang agak menarik.
Titik injeksi ketergantungan tidak hanya untuk memastikan bahwa setiap aktor yang membutuhkan sumber daya dapat memilikinya, karena jelas, jika Anda membuat semua sumber daya global, maka setiap aktor akan memiliki akses ke setiap sumber daya, masalah diselesaikan, bukan?
Titik injeksi ketergantungan adalah:
Fakta bahwa dalam konfigurasi khusus Anda, semua aktor kebetulan memerlukan akses ke sumber daya yang sama tidak relevan. Percayalah, suatu hari Anda akan perlu mengkonfigurasi ulang hal-hal sehingga para aktor akan memiliki akses ke berbagai contoh sumber daya, dan kemudian Anda akan menyadari bahwa Anda telah melukis diri sendiri di sudut. Beberapa jawaban sudah menunjuk konfigurasi seperti itu: pengujian .
Contoh lain: misalkan Anda membagi aplikasi Anda menjadi client-server. Semua aktor di klien menggunakan set sumber daya pusat yang sama pada klien, dan semua aktor di server menggunakan set sumber daya pusat yang sama di server. Sekarang anggaplah, suatu hari, bahwa Anda memutuskan untuk membuat versi "mandiri" dari aplikasi server-klien Anda, di mana klien dan server dikemas dalam satu executable dan berjalan di mesin virtual yang sama. (Atau lingkungan runtime, tergantung pada bahasa pilihan Anda.)
Jika Anda menggunakan injeksi dependensi, Anda dapat dengan mudah memastikan bahwa semua aktor klien diberi instance sumber daya klien untuk bekerja dengannya, sementara semua aktor server menerima instance sumber daya server.
Jika Anda tidak menggunakan injeksi ketergantungan, Anda benar-benar kurang beruntung, karena hanya satu instance global dari setiap sumber daya yang dapat ada dalam satu mesin virtual.
Kemudian, Anda harus mempertimbangkan: apakah semua aktor benar-benar membutuhkan akses ke sumber daya itu? sangat?
Ada kemungkinan bahwa Anda telah membuat kesalahan dengan mengubah sumber daya itu menjadi objek dewa, (jadi, tentu saja semua orang membutuhkan akses ke sana), atau mungkin Anda terlalu melebih-lebihkan jumlah aktor dalam proyek Anda yang benar - benar membutuhkan akses ke sumber daya itu. .
Dengan global, setiap baris kode sumber di seluruh aplikasi Anda memiliki akses ke setiap sumber daya global. Dengan injeksi ketergantungan, setiap instance sumber daya hanya dapat dilihat oleh para aktor yang benar-benar membutuhkannya. Jika keduanya sama, (aktor yang membutuhkan sumber daya tertentu terdiri dari 100% dari baris kode sumber dalam proyek Anda,) maka Anda pasti telah membuat kesalahan dalam desain Anda. Jadi, baik
Refactor sumber daya dewa besar yang besar menjadi sub-sumber daya yang lebih kecil, sehingga aktor yang berbeda memerlukan akses ke bagian yang berbeda, tetapi jarang aktor membutuhkan semua bagiannya, atau
Refactor aktor Anda untuk menerima sebagai parameter hanya himpunan bagian dari masalah yang perlu mereka selesaikan, sehingga mereka tidak harus berkonsultasi dengan sumber daya besar yang sangat besar sepanjang waktu.
sumber
Itu adalah klaim yang meragukan. Tautan yang Anda gunakan sebagai bukti merujuk pada negara yang bisa berubah - global. Mereka jelas jahat. Baca saja global hanya konstanta. Konstanta relatif waras. Maksud saya, Anda tidak akan menyuntikkan nilai pi ke semua kelas Anda, bukan?
Tidak, mereka tidak.
Jika fungsi / kelas Anda memiliki terlalu banyak dependensi, orang merekomendasikan untuk berhenti dan melihat mengapa desain Anda sangat buruk. Memiliki banyak dependensi merupakan tanda bahwa kelas / fungsi Anda mungkin melakukan terlalu banyak hal dan / atau Anda tidak memiliki abstraksi yang memadai.
Ini adalah mimpi buruk desain. Anda memiliki banyak hal yang dapat digunakan / disalahgunakan tanpa cara validasi, tidak ada batasan konkurensi - itu hanya penggabungan seluruh.
Ya, memiliki penggabungan ke data umum di mana - mana tidak terlalu berbeda dari global, dan karenanya, masih buruk.
Sisi positifnya adalah Anda memisahkan konsumen dari variabel global itu sendiri. Ini memberi Anda beberapa fleksibilitas dalam bagaimana instance dibuat. Itu juga tidak memaksa Anda untuk hanya memiliki satu instance. Anda dapat membuat yang berbeda untuk diteruskan ke konsumen yang berbeda. Anda juga dapat membuat implementasi yang berbeda dari antarmuka Anda per konsumen jika Anda perlu.
Dan karena perubahan yang tak terhindarkan datang dengan persyaratan bergeser dari perangkat lunak, tingkat fleksibilitas dasar seperti itu sangat penting .
sumber
foo
s"?Ada tiga alasan utama yang harus Anda pertimbangkan.
Jadi, ringkasnya: DI bukan tujuan, itu hanya alat yang memungkinkan untuk mencapai tujuan. Jika Anda menyalahgunakannya (seperti yang Anda lakukan dalam contoh), Anda tidak akan mendekati tujuan, tidak ada properti magis DI yang akan membuat desain yang buruk menjadi baik.
sumber
Ini benar-benar tentang testability - biasanya objek global sangat mudah dikelola dan mudah dibaca / dimengerti (setelah Anda tahu di sana, tentu saja) tetapi itu adalah perlengkapan permanen dan ketika Anda datang untuk membagi kelas Anda menjadi tes terisolasi, Anda menemukan bahwa Anda objek global macet mengatakan "bagaimana dengan saya" dan dengan demikian tes terisolasi Anda memerlukan objek global untuk dimasukkan di dalamnya. Tidak membantu bahwa sebagian besar kerangka kerja pengujian tidak dengan mudah mendukung skenario ini.
Jadi ya, ini masih bersifat global. Alasan mendasar untuk memilikinya tidak berubah, hanya cara mengaksesnya.
Adapun inisialisasi, ini masih menjadi masalah - Anda masih harus mengatur konfigurasi agar tes Anda dapat berjalan, sehingga Anda tidak mendapatkan apa pun di sana. Saya kira Anda dapat membagi objek bergantung besar menjadi banyak yang lebih kecil dan meneruskan yang sesuai ke kelas yang membutuhkannya, tetapi Anda mungkin mendapatkan kompleksitas yang lebih besar daripada manfaat apa pun.
Terlepas dari semua itu, ini adalah contoh dari 'ekor mengibas-ngibaskan anjing', kebutuhan untuk menguji mendorong bagaimana basis kode dirancang (masalah serupa muncul dengan item seperti kelas statis, misalnya datetime saat ini).
Secara pribadi saya lebih suka menempelkan semua data global saya di objek global (atau beberapa) tetapi menyediakan aksesor untuk mendapatkan bit yang dibutuhkan kelas saya. Lalu saya bisa mengejek mereka untuk mengembalikan data yang saya inginkan ketika datang ke pengujian tanpa menambah kompleksitas DI.
sumber
Selain jawaban yang sudah Anda miliki,
Salah satu karakteristik pemrograman OOP adalah hanya menyimpan properti yang benar-benar Anda butuhkan untuk objek tertentu,
SEKARANG JIKA objek Anda memiliki terlalu banyak properti - Anda harus memecah objek Anda lebih jauh menjadi sub-objek.
Edit
Sudah disebutkan mengapa objek global buruk, saya akan menambahkan satu contoh untuk itu.
Anda perlu melakukan pengujian unit pada kode GUI formulir windows, jika Anda menggunakan global, Anda harus membuat semua tombol dan acara itu misalnya untuk membuat kasus pengujian yang tepat ...
sumber