Saya menonton presentasi Google Tech Talk tentang Unit Testing , yang diberikan oleh Misko Hevery, dan dia berkata untuk menghindari penggunaan new
kata kunci dalam kode logika bisnis.
Saya menulis sebuah program, dan akhirnya saya menggunakan new
kata kunci di sana-sini, tetapi kebanyakan untuk instantiating objek yang menyimpan data (yaitu, mereka tidak memiliki fungsi atau metode).
Saya bertanya-tanya, apakah saya melakukan sesuatu yang salah ketika saya menggunakan kata kunci baru untuk program saya. Dan di mana kita bisa melanggar 'aturan' itu?
Jawaban:
Ini lebih dari sekadar panduan keras dan cepat.
Dengan menggunakan "baru" dalam kode produksi Anda, Anda menggabungkan kelas Anda dengan kolaboratornya. Jika seseorang ingin menggunakan beberapa kolaborator lain, misalnya semacam kolaborator tiruan untuk pengujian unit, mereka tidak bisa - karena kolaborator dibuat dalam logika bisnis Anda.
Tentu saja, seseorang perlu membuat objek-objek baru ini, tetapi ini sering kali lebih baik diserahkan kepada salah satu dari dua tempat: kerangka kerja injeksi ketergantungan seperti Spring, atau di mana kelas mana pun yang membuat kelas logika bisnis Anda, disuntikkan melalui konstruktor.
Tentu saja, Anda bisa mengambil ini terlalu jauh. Jika Anda ingin mengembalikan ArrayList baru, maka Anda mungkin OK - terutama jika ini akan menjadi Daftar abadi.
Pertanyaan utama yang harus Anda tanyakan pada diri sendiri adalah "apakah tanggung jawab utama dari sedikit kode ini untuk membuat objek jenis ini, atau apakah ini hanya detail implementasi yang bisa saya pindah ke tempat lain?"
sumber
Collections.unmodifiableList
atau sesuatu. Tapi saya tahu maksud Anda :)Inti dari pertanyaan ini adalah di akhir: Saya bertanya-tanya, apakah saya melakukan sesuatu yang salah ketika saya menggunakan kata kunci baru untuk program saya. Dan di mana kita bisa melanggar 'aturan' itu?
Jika Anda dapat menulis unit test yang efektif untuk kode Anda, maka Anda tidak melakukan kesalahan. Jika penggunaan Anda
new
membuatnya sulit atau tidak mungkin untuk menguji kode Anda, maka Anda harus mengevaluasi kembali penggunaan baru Anda. Anda dapat memperluas analisis ini untuk berinteraksi dengan kelas lain, tetapi kemampuan untuk menulis tes unit yang solid seringkali merupakan proxy yang cukup baik.sumber
Singkatnya, setiap kali Anda menggunakan "baru", Anda dengan erat menyambungkan kelas yang berisi kode ini ke objek yang sedang dibuat; untuk instantiate salah satu objek ini, kelas yang melakukan instantiasi harus tahu tentang kelas konkret yang sedang dipakai. Jadi, ketika menggunakan "baru", Anda harus mempertimbangkan apakah kelas tempat Anda meletakkan instantiasi adalah tempat "baik" untuk tempat pengetahuan itu berada, dan Anda bersedia untuk membuat perubahan di bidang ini jika bentuk dari objek yang dipakai adalah untuk berubah.
Kopling ketat, yang merupakan objek yang memiliki pengetahuan kelas konkret lainnya, tidak selalu harus dihindari; pada tingkat tertentu, sesuatu, DI MANA SAJA, harus tahu cara membuat objek ini, bahkan jika segala sesuatu berurusan dengan objek dengan diberi salinannya dari tempat lain. Namun, ketika kelas yang dibuat berubah, setiap kelas yang tahu tentang implementasi konkret dari kelas itu harus diperbarui untuk menangani dengan benar perubahan kelas itu.
Pertanyaan yang harus selalu Anda tanyakan adalah, "Apakah kelas ini tahu cara membuat kelas lain ini menjadi tanggung jawab saat mempertahankan aplikasi?" Kedua metodologi desain utama (SOLID dan GRASP) biasanya akan menjawab "ya", untuk alasan yang agak berbeda. Namun, mereka hanya metodologi, dan keduanya memiliki batasan ekstrem yang tidak dirumuskan berdasarkan pengetahuan program unik Anda. Dengan demikian, mereka hanya bisa keliru pada sisi hati-hati, dan menganggap bahwa setiap titik kopling ketat akan BENAR-BENAR menyebabkan Anda masalah terkait dengan membuat perubahan pada salah satu atau kedua sisi dari titik ini. Anda harus membuat keputusan akhir dengan mengetahui tiga hal; praktik terbaik teoretis (yaitu secara longgar menggabungkan segala sesuatu karena apa pun dapat berubah); biaya penerapan praktik terbaik teoretis (yang mungkin mencakup beberapa lapisan abstraksi baru yang akan memudahkan satu jenis perubahan sementara menghambat yang lain); dan probabilitas dunia nyata bahwa jenis perubahan yang Anda harapkan akan diperlukan.
Beberapa pedoman umum:
Hindari hubungan erat antara pustaka kode yang dikompilasi. Antarmuka antara DLL (atau EXE dan DLLnya) adalah tempat utama di mana kopling ketat akan menghadirkan kerugian. Jika Anda membuat perubahan ke kelas A di DLL X, dan kelas B di EXE utama tahu tentang kelas A, Anda harus mengkompilasi ulang dan melepaskan kedua binari. Dalam satu biner, kopling yang lebih ketat umumnya lebih diizinkan karena seluruh biner harus dibangun kembali untuk setiap perubahan. Kadang-kadang, harus membangun kembali banyak biner tidak dapat dihindari, tetapi Anda harus menyusun kode Anda sehingga Anda dapat menghindarinya jika memungkinkan, terutama untuk situasi di mana bandwidth berada pada premium (seperti menggunakan aplikasi seluler; mendorong DLL baru dalam peningkatan jauh lebih murah) daripada mendorong seluruh program).
Hindari hubungan erat antara "pusat-pusat logika" utama dari program Anda. Anda dapat menganggap program terstruktur dengan baik sebagai terdiri dari irisan horizontal dan vertikal. Irisan horizontal dapat berupa tingkatan aplikasi tradisional, seperti UI, Pengendali, Domain, DAO, Data; irisan vertikal dapat didefinisikan untuk jendela atau tampilan individual, atau untuk "cerita pengguna" individual (seperti membuat catatan baru dari beberapa tipe dasar). Saat melakukan panggilan yang bergerak ke atas, ke bawah, ke kiri atau ke kanan dalam sistem yang terstruktur dengan baik, Anda biasanya harus mengabstraksikan panggilan tersebut. Misalnya, ketika validasi perlu mengambil data, seharusnya tidak memiliki akses ke DB secara langsung tetapi harus membuat panggilan ke antarmuka untuk pengambilan data, yang didukung oleh objek aktual yang tahu bagaimana melakukan ini. Ketika beberapa kontrol UI perlu melakukan logika lanjutan yang melibatkan jendela lain, harus abstrak pemicu logika ini melalui suatu peristiwa dan / atau panggilan balik; itu tidak harus tahu apa yang akan dilakukan sebagai hasilnya, memungkinkan Anda untuk mengubah apa yang akan dilakukan tanpa mengubah kontrol yang memicunya.
Dalam setiap kasus, pertimbangkan seberapa mudah atau sulit perubahan akan dibuat, dan seberapa besar kemungkinan perubahan itu akan terjadi. Jika suatu objek yang Anda buat hanya pernah digunakan dari satu tempat, dan Anda tidak melihat perubahan itu, maka kopling ketat umumnya lebih diizinkan, dan bahkan mungkin lebih unggul dalam situasi ini dari kopling longgar. Kopling longgar membutuhkan abstraksi, yang merupakan lapisan tambahan yang mencegah perubahan pada objek dependen ketika implementasi dependensi harus berubah. Namun, jika antarmuka itu sendiri harus berubah (menambahkan panggilan metode baru, atau menambahkan parameter ke panggilan metode yang ada), maka antarmuka sebenarnya meningkatkan jumlah pekerjaan yang diperlukan untuk melakukan perubahan. Anda harus mempertimbangkan kemungkinan berbagai jenis perubahan yang berdampak pada desain,
sumber
Objek yang hanya menyimpan data, atau DTO (objek transfer data) baik-baik saja, buat sepanjang hari. Di mana Anda ingin menghindari
new
adalah pada kelas-kelas yang melakukan tindakan, Anda ingin kelas-kelas mengkonsumsi bukannya memprogram melawan antarmuka , di mana mereka dapat memanggil logika itu dan mengkonsumsinya, tetapi tidak bertanggung jawab untuk benar-benar membuat instance kelas-kelas yang mengandung logika . Mereka yang melakukan tindakan atau kelas yang mengandung logika adalah dependensi. Pembicaraan Misko diarahkan pada Anda menyuntikkan dependensi itu dan membiarkan beberapa entitas lain bertanggung jawab untuk benar-benar membuatnya.sumber
Yang lain telah menyebutkan poin ini, tetapi ingin mengutip ini dari Kode Bersih Paman Bob (Bob Martin) karena mungkin akan membuatnya lebih mudah untuk memahami konsep:
"Mekanisme kuat untuk memisahkan konstruksi dari penggunaan adalah Dependency Injection (DI), penerapan Inversion of Control (IoC) ke manajemen ketergantungan. Inversi Control memindahkan tanggung jawab sekunder dari suatu objek ke objek lain yang didedikasikan untuk tujuan tersebut, dengan demikian mendukung yang tanggung jawab single Prinsip . dalam konteks manajemen ketergantungan, sebuah objek tidak harus mengambil tanggung jawab untuk instantiating dependensi itu sendiri. Sebaliknya, harus lulus tanggung jawab ini ke mekanisme lain "berwibawa", demikian pembalik kontrol. karena setup keprihatinan global, ini mekanisme otoritatif biasanya akan menjadi rutin "utama" atau wadah tujuan khusus. "
Saya pikir itu selalu ide yang baik untuk mengikuti aturan ini. Yang lain menyebutkan yang lebih penting IMO -> itu memisahkan kelas Anda dari dependensinya (kolaborator). Alasan kedua adalah karena membuat kelas Anda lebih kecil dan terser, lebih mudah dipahami dan bahkan digunakan kembali dalam konteks lain.
sumber