Rekan-rekan saya suka mengatakan "logging / caching / dll. Adalah masalah lintas sektoral" dan kemudian melanjutkan menggunakan singleton yang sesuai di mana-mana. Namun mereka menyukai IoC dan DI.
Apakah ini benar-benar alasan yang sah untuk melanggar prinsip SOLI D ?
Jawaban:
Tidak.
SOLID ada sebagai pedoman untuk memperhitungkan perubahan yang tak terhindarkan. Apakah Anda benar - benar tidak akan pernah mengubah perpustakaan logging Anda, atau target, atau pemfilteran, atau pemformatan, atau ...? Apakah Anda benar - benar tidak akan mengubah perpustakaan caching Anda, atau target, atau strategi, atau pelingkupan, atau ...?
Tentu saja kamu. Paling tidak , Anda akan ingin mengejek hal-hal ini dengan cara yang waras untuk mengisolasi mereka untuk pengujian. Dan jika Anda ingin mengisolasi mereka untuk pengujian, Anda kemungkinan akan mengalami alasan bisnis di mana Anda ingin mengisolasi mereka untuk alasan kehidupan nyata.
Dan Anda kemudian akan mendapatkan argumen bahwa logger itu sendiri akan menangani perubahan. "Oh, jika target / filtering / format / strategi berubah, maka kita hanya akan mengubah konfigurasi!" Itu sampah. Anda tidak hanya sekarang memiliki Obyek Tuhan yang menangani semua hal ini, Anda juga menulis kode Anda dalam XML (atau serupa) di mana Anda tidak mendapatkan analisis statis, Anda tidak mendapatkan kesalahan waktu kompilasi, dan Anda tidak Saya tidak benar-benar mendapatkan tes unit yang efektif.
Apakah ada kasus yang melanggar pedoman SOLID? Benar. Kadang-kadang hal - hal tidak akan berubah (tanpa memerlukan penulisan ulang penuh). Kadang-kadang sedikit pelanggaran terhadap LSP adalah solusi terbersih. Terkadang membuat antarmuka yang terisolasi tidak memberikan nilai.
Tetapi penebangan dan caching (dan masalah lintas sektoral lainnya) tidak demikian. Biasanya itu adalah contoh bagus dari masalah kopling dan desain yang Anda dapatkan ketika Anda mengabaikan pedoman.
sumber
String
atauInt32
atau bahkanList
keluar dari modul Anda. Hanya ada sejauh mana masuk akal dan waras untuk merencanakan perubahan. Dan, di luar tipe "inti" yang paling jelas, membedakan apa yang mungkin Anda ubah sebenarnya hanyalah masalah pengalaman dan penilaian.Iya
Ini adalah inti keseluruhan dari istilah "kepedulian lintas sektoral" - ini berarti sesuatu yang tidak sesuai dengan prinsip SOLID.
Di sinilah idealisme bertemu dengan kenyataan.
Orang-orang semi-baru untuk SOLID dan lintas sektoral sering mengalami tantangan mental ini. Tidak apa-apa, jangan panik. Berusaha keras untuk memasukkan semuanya ke dalam istilah SOLID, tetapi ada beberapa tempat seperti logging dan caching di mana SOLID tidak masuk akal. Cross-cutting adalah saudara laki-laki SOLID, mereka berjalan beriringan.
sumber
HttpContextBase
(yang diperkenalkan untuk alasan ini). Saya tahu pasti bahwa kenyataan saya akan benar-benar masam tanpa kelas ini.Untuk logging saya pikir itu. Logging bersifat meresap dan umumnya tidak terkait dengan fungsionalitas layanan. Sudah umum dan dipahami dengan baik untuk menggunakan pola singleton kerangka logging. Jika tidak, Anda membuat dan menyuntikkan penebang di mana - mana dan Anda tidak menginginkannya.
Satu masalah dari atas adalah bahwa seseorang akan berkata 'tetapi bagaimana saya bisa menguji penebangan?' . Pikiran saya adalah bahwa saya biasanya tidak menguji logging, di luar menyatakan bahwa saya benar-benar dapat membaca file log dan memahaminya. Ketika saya melihat logging diuji, biasanya karena seseorang perlu menegaskan bahwa suatu kelas telah benar-benar melakukan sesuatu dan mereka menggunakan pesan log untuk mendapatkan umpan balik itu. Saya lebih suka mendaftarkan beberapa pendengar / pengamat di kelas itu dan menegaskan dalam tes saya bahwa yang dipanggil. Anda kemudian dapat memasukkan acara Anda masuk ke pengamat itu.
Saya pikir caching adalah skenario yang sama sekali berbeda.
sumber
2 sen saya ...
Iya dan tidak.
Anda seharusnya tidak pernah benar - benar melanggar prinsip-prinsip yang Anda adopsi; tetapi, prinsip Anda harus selalu diberi nuansa dan diadopsi untuk mencapai tujuan yang lebih tinggi. Jadi, dengan pemahaman yang dikondisikan dengan baik, beberapa jelas pelanggaran mungkin tidak aktual pelanggaran "roh" atau "isi prinsip secara keseluruhan."
Prinsip-prinsip SOLID khususnya, selain membutuhkan banyak nuansa, pada akhirnya tunduk pada tujuan "memberikan perangkat lunak yang berfungsi dan dapat dirawat." Jadi, mematuhi prinsip SOLID tertentu adalah merugikan diri sendiri dan bertentangan dengan diri sendiri ketika melakukan hal tersebut bertentangan dengan tujuan SOLID. Dan di sini, saya sering perhatikan bahwa memberikan mengalahkan rawatan .
Jadi, bagaimana dengan D di SOLID ? Yah, itu berkontribusi pada peningkatan rawatan dengan membuat modul yang dapat digunakan kembali relatif agnostik untuk konteksnya. Dan kita dapat mendefinisikan "modul yang dapat digunakan kembali" sebagai "kumpulan kode yang Anda rencanakan untuk digunakan dalam konteks lain yang berbeda." Dan itu berlaku untuk satu fungsi, kelas, set kelas, dan program.
Dan ya, mengubah implementasi logger mungkin menempatkan modul Anda ke dalam "konteks lain yang berbeda."
Jadi, izinkan saya menawarkan dua peringatan besar saya :
Pertama: Menggambar garis di sekitar blok kode yang merupakan "modul yang dapat digunakan kembali" adalah masalah penilaian profesional. Dan penilaian Anda tentu terbatas pada pengalaman Anda.
Jika Anda tidak saat ini berencana untuk menggunakan modul dalam konteks lain, itu adalah mungkin OK untuk itu bergantung tak berdaya di atasnya. Peringatan ke peringatan: Rencana Anda mungkin salah - tetapi itu juga OK. Semakin lama Anda menulis modul demi modul, semakin intuitif dan akurat perasaan Anda apakah "Aku akan membutuhkan ini lagi suatu hari nanti". Tetapi, Anda mungkin tidak akan pernah bisa secara retrospektif berkata, "Saya telah memodulasi dan memisahkan segala sesuatu sejauh mungkin, tetapi tanpa kelebihan ."
Jika Anda merasa bersalah tentang kesalahan penilaian Anda, buka pengakuan dan lanjutkan ...
Kedua: Pembalikan kontrol tidak sama dengan ketergantungan injeksi .
Ini terutama benar ketika Anda mulai menyuntikkan dependensi dan mual . Dependency Injection adalah taktik yang berguna untuk strategi IoC yang menyeluruh. Tapi, saya berpendapat bahwa DI kurang manjur dibandingkan beberapa taktik lain - seperti menggunakan antarmuka dan adaptor - satu titik paparan konteks dari dalam modul.
Dan mari kita benar-benar fokus pada ini sebentar. Karena, walaupun Anda menyuntikkan
Logger
mual iklan , Anda harus menulis kode padaLogger
antarmuka. Anda tidak dapat mulai menggunakan yang baruLogger
dari vendor yang berbeda yang mengambil parameter dalam urutan yang berbeda. Kemampuan itu berasal dari pengkodean, di dalam modul, terhadap antarmuka yang ada di dalam modul dan yang memiliki submodule tunggal (Adaptor) di dalamnya untuk mengelola ketergantungan.Dan jika Anda mengkode terhadap Adaptor, apakah
Logger
disuntikkan ke Adaptor itu atau ditemukan oleh Adaptor umumnya sangat tidak signifikan untuk tujuan rawatan keseluruhan. Dan yang lebih penting, jika Anda memiliki Adaptor tingkat modul, mungkin hanya masuk akal untuk menyuntikkannya ke apa saja. Ini ditulis untuk modul.tl; dr - Berhentilah meributkan prinsip tanpa mempertimbangkan mengapa Anda menggunakan prinsip tersebut. Dan, lebih praktisnya, hanya membangun sebuah
Adapter
untuk setiap modul. Gunakan penilaian Anda saat memutuskan di mana Anda menggambar batas "modul". Dari dalam setiap modul, silakan dan merujuk langsung keAdapter
. Dan tentu saja, menyuntikkan logger nyata keAdapter
- tetapi tidak ke setiap hal kecil yang mungkin membutuhkannya.sumber
Gagasan bahwa penebangan harus selalu dilaksanakan sebagai singleton adalah salah satu kebohongan yang telah diberitahukan begitu sering sehingga mendapatkan daya tarik.
Selama sistem operasi modern telah dikenali, Anda mungkin ingin masuk ke banyak tempat, tergantung pada sifat keluarannya .
Perancang sistem harus terus-menerus mempertanyakan kemanjuran solusi masa lalu sebelum secara membabi buta memasukkannya dalam yang baru. Jika mereka tidak melakukan ketekunan seperti itu, maka mereka tidak melakukan pekerjaan mereka.
sumber
Penebangan yang benar-benar merupakan kasus khusus.
@Telastyn menulis:
Jika Anda mengantisipasi bahwa Anda mungkin perlu mengubah perpustakaan logging Anda, maka Anda harus menggunakan fasad; yaitu SLF4J jika Anda berada di dunia Java.
Adapun sisanya, perpustakaan logging yang layak menangani perubahan ke mana logging pergi, peristiwa apa yang disaring, bagaimana peristiwa log diformat menggunakan file konfigurasi logger dan (jika perlu) kelas plugin kustom. Ada sejumlah alternatif di luar rak.
Singkatnya, ini adalah masalah yang diselesaikan ... untuk logging ... dan karenanya tidak perlu menggunakan Dependency Injection untuk menyelesaikannya.
Satu-satunya kasus di mana DI mungkin bermanfaat (lebih dari pendekatan pendataan standar) adalah jika Anda ingin menjadikan pendataan aplikasi Anda ke unit testing. Namun, saya menduga sebagian besar pengembang akan mengatakan bahwa logging bukan bagian dari fungsi kelas, dan bukan sesuatu yang perlu diuji.
@Telastyn menulis:
Saya khawatir itu adalah teori yang sangat teoritis. Dalam praktiknya, sebagian besar pengembang dan integrator sistem SEPERTI fakta bahwa Anda dapat mengonfigurasi pencatatan melalui file konfigurasi. Dan mereka SUKA fakta bahwa mereka tidak diharapkan untuk menguji unit logging modul.
Tentu, jika Anda mengisi konfigurasi logging maka Anda bisa mendapatkan masalah, tetapi mereka akan bermanifestasi sebagai salah satu aplikasi gagal selama startup atau terlalu banyak / terlalu sedikit logging. 1) Masalah-masalah ini mudah diperbaiki dengan memperbaiki kesalahan dalam file konfigurasi. 2) Alternatifnya adalah siklus build / analysis / test / deploy lengkap setiap kali Anda membuat perubahan ke level logging. Itu tidak bisa diterima.
sumber
Ya dan Tidak !
Ya: Saya pikir masuk akal bahwa subsistem yang berbeda (atau lapisan semantik atau pustaka atau gagasan bundling modular lainnya) masing-masing menerima (yang sama atau) logger yang mungkin berbeda selama inisialisasi mereka daripada semua subsistem yang mengandalkan singleton bersama yang sama .
Namun,
Tidak: pada saat yang sama tidak masuk akal untuk membuat parameter logging di setiap objek kecil (dengan konstruktor atau metode instance). Untuk menghindari mengasapi yang tidak perlu dan tidak berguna, entitas yang lebih kecil harus menggunakan logger singleton dari konteks terlampir mereka.
Ini adalah salah satu alasan di antara banyak orang lain untuk memikirkan modularitas dalam level: metode digabungkan ke dalam kelas, sementara kelas digabungkan ke dalam subsistem dan / atau lapisan semantik. Bundel yang lebih besar ini adalah alat abstraksi yang berharga; kita harus memberikan pertimbangan yang berbeda dalam batas-batas modular daripada ketika melintasinya.
sumber
Pertama dimulai dengan cache singleton yang kuat, hal-hal berikutnya yang Anda lihat adalah singleton yang kuat untuk lapisan basis data yang memperkenalkan keadaan global, API non-deskriptif
class
es dan kode yang tidak dapat diuji.Jika Anda memutuskan untuk tidak memiliki singleton untuk basis data, mungkin bukan ide yang baik untuk memiliki singleton untuk cache, bagaimanapun, mereka mewakili konsep yang sangat mirip, penyimpanan data, hanya menggunakan mekanisme yang berbeda.
Menggunakan singleton di kelas ternyata kelas yang memiliki jumlah dependensi tertentu ke kelas yang secara teoritis memiliki jumlah tak terbatas, karena Anda tidak pernah tahu apa yang sebenarnya tersembunyi di balik metode statis.
Dalam dekade terakhir saya telah menghabiskan pemrograman, hanya ada satu kasus di mana saya menyaksikan upaya untuk mengubah logika logging (yang kemudian ditulis sebagai singleton). Jadi, meskipun saya menyukai suntikan ketergantungan, penebangan sebenarnya bukan masalah besar. Cache, di sisi lain, saya pasti akan selalu menjadikannya sebagai ketergantungan.
sumber
Ya dan Tidak, tetapi kebanyakan Tidak
Saya menganggap sebagian besar percakapan didasarkan pada contoh statis vs disuntikkan. Tidak ada yang mengusulkan agar penebangan merusak SRP saya kira? Kita terutama berbicara tentang "prinsip inversi ketergantungan". Tbh saya sebagian besar setuju dengan tidak ada jawaban Telastyn.
Kapan boleh menggunakan statika? Karena jelas itu kadang oke. Jawaban ya poin dari manfaat abstraksi dan jawaban "tidak" menunjukkan bahwa itu adalah sesuatu yang Anda bayar. Salah satu alasan mengapa pekerjaan Anda sulit adalah karena tidak ada satu jawaban yang dapat Anda tulis dan terapkan pada semua situasi.
Mengambil:
Convert.ToInt32("1")
Saya lebih suka ini:
Mengapa? Saya menerima bahwa saya perlu memperbaiki kode jika saya memerlukan fleksibilitas untuk menukar kode konversi. Saya menerima bahwa saya tidak akan bisa mengejek ini. Saya percaya bahwa kesederhanaan dan kesederhanaan sepadan dengan perdagangan ini.
Melihat ujung spektrum yang lain, jika
IConverter
aSqlConnection
, saya akan cukup ngeri melihatnya sebagai panggilan statis. Alasan mengapa itu jelas sekali. Saya akan menunjukkan bahwa aSQLConnection
bisa cukup "lintas silang" dalam aplikasi, jadi saya tidak akan menggunakan kata-kata yang tepat.Apakah Logging lebih seperti a
SQLConnection
atauConvert.ToInt32
? Saya akan mengatakan lebih seperti 'SQLConnection`.Anda seharusnya mengejek Logging . Ia berbicara kepada dunia luar. Saat menulis metode menggunakan
Convert.ToIn32
, saya menggunakannya sebagai alat untuk menghitung beberapa output lain yang dapat diuji secara terpisah dari kelas. Saya tidak perlu memeriksaConvert
dipanggil dengan benar ketika memeriksa bahwa "1" + "2" == "3". Penebangan berbeda, ini merupakan output kelas yang sepenuhnya independen. Saya berasumsi ini adalah output yang memiliki nilai bagi Anda, tim pendukung, dan bisnis. Kelas Anda tidak berfungsi jika logging tidak benar, sehingga unit test tidak boleh lulus. Anda harus menguji apa yang dicatat log kelas Anda. Saya pikir ini adalah argumen pembunuh, saya benar-benar bisa berhenti di sini.Saya juga berpikir itu adalah sesuatu yang sangat mungkin berubah. Pencatatan yang baik tidak hanya mencetak string, ini adalah pandangan tentang apa yang sedang dilakukan aplikasi Anda (saya penggemar pencatatan berdasarkan peristiwa). Saya telah melihat pembuatan log dasar berubah menjadi UI pelaporan yang cukup rumit. Jelas jauh lebih mudah untuk menuju ke arah ini jika logging Anda terlihat seperti
_logger.Log(new ApplicationStartingEvent())
dan kurang disukaiLogger.Log("Application has started")
. Orang mungkin berpendapat bahwa ini adalah menciptakan inventaris untuk masa depan yang mungkin tidak pernah terjadi, ini adalah panggilan penilaian dan kebetulan saya pikir itu layak.Bahkan dalam proyek pribadi saya, saya membuat UI non logging murni menggunakan
_logger
untuk mencari tahu apa yang sedang dilakukan aplikasi. Ini berarti saya tidak perlu menulis kode untuk mencari tahu apa yang sedang dilakukan aplikasi, dan saya berakhir dengan logging yang solid. Saya merasa seolah-olah sikap saya terhadap penebangan sederhana dan tidak berubah, gagasan itu tidak akan terjadi pada saya.Jadi saya setuju dengan Telastyn untuk kasus logging.
sumber
Semantic Logging Application Block
. Jangan menggunakannya, seperti sebagian besar kode yang dibuat oleh tim "pola dan praktik" MS, ironisnya, ini adalah antipattern.Perhatian Lintas potong pertama bukanlah blok bangunan utama dan tidak seharusnya diperlakukan sebagai ketergantungan dalam suatu sistem. Suatu sistem harus bekerja jika mis. Logger tidak diinisialisasi atau cache tidak berfungsi. Bagaimana Anda membuat sistem lebih sedikit digabungkan dan kohesif? Di situlah SOLID muncul dalam desain sistem OO.
Menjaga objek sebagai singleton tidak ada hubungannya dengan SOLID. Itulah siklus hidup objek Anda, berapa lama Anda ingin objek itu hidup dalam memori.
Kelas yang membutuhkan dependensi untuk diinisialisasi seharusnya tidak tahu apakah instance kelas yang disediakan adalah singleton atau sementara. Tapi tldr; jika Anda menulis Logger.Instance.Log () di setiap metode atau kelas maka itu kode bermasalah (bau kode / kopling keras) yang benar-benar berantakan. Ini adalah saat ketika orang mulai menyalahgunakan SOLID. Dan sesama pengembang seperti OP mulai mengajukan pertanyaan asli seperti ini.
sumber
Saya telah memecahkan masalah ini menggunakan kombinasi pewarisan dan sifat-sifat (juga disebut mixin dalam beberapa bahasa). Ciri-ciri sangat berguna untuk menyelesaikan masalah lintas bidang seperti ini. Meskipun biasanya fitur bahasa, jadi saya pikir jawaban sebenarnya adalah itu tergantung pada fitur bahasa.
sumber