Catatan: Sampel kode ditulis dalam c #, tetapi itu tidak masalah. Saya telah menempatkan c # sebagai tag karena saya tidak dapat menemukan yang lebih tepat. Ini tentang struktur kode.
Saya membaca Kode Bersih dan berusaha menjadi programmer yang lebih baik.
Saya sering menemukan diri saya berjuang untuk mengikuti Prinsip Tanggung Jawab Tunggal (kelas dan fungsi harus melakukan hanya satu hal), khususnya dalam fungsi. Mungkin masalah saya adalah "satu hal" tidak didefinisikan dengan baik, tetapi masih ...
Contoh: Saya punya daftar Fluffies di database. Kami tidak peduli apa itu Fluffy. Saya ingin kelas memulihkan bulu. Namun, bulu bisa berubah sesuai dengan beberapa logika. Bergantung pada beberapa logika, kelas ini akan mengembalikan data dari cache atau mendapatkan yang terbaru dari database. Kita dapat mengatakan bahwa ia mengelola bulu, dan itu adalah satu hal. Untuk membuatnya sederhana, katakanlah data yang dimuat baik untuk satu jam, dan kemudian harus dimuat ulang.
class FluffiesManager
{
private Fluffies m_Cache;
private DateTime m_NextReload = DateTime.MinValue;
// ...
public Fluffies GetFluffies()
{
if (NeedsReload())
LoadFluffies();
return m_Cache;
}
private NeedsReload()
{
return (m_NextReload < DateTime.Now);
}
private void LoadFluffies()
{
GetFluffiesFromDb();
UpdateNextLoad();
}
private void UpdateNextLoad()
{
m_NextReload = DatTime.Now + TimeSpan.FromHours(1);
}
// ...
}
GetFluffies()
sepertinya tidak masalah bagi saya. Pengguna meminta beberapa fluffies, kami menyediakannya. Akan memulihkan mereka dari DB jika diperlukan, tetapi itu bisa dianggap sebagai bagian dari mendapatkan fluffies (tentu saja, itu agak subyektif).
NeedsReload()
sepertinya benar juga. Memeriksa apakah kita perlu memuat ulang fluffies. UpdateNextLoad baik-baik saja. Memperbarui waktu untuk memuat ulang berikutnya. itu pasti satu hal.
Namun, saya merasa apa LoadFluffies()
yang tidak bisa digambarkan sebagai satu hal. Itu mendapatkan data dari Database, dan menjadwalkan memuat ulang berikutnya. Sulit untuk membantah bahwa menghitung waktu untuk memuat ulang berikutnya adalah bagian dari mendapatkan data. Namun, saya tidak dapat menemukan cara yang lebih baik untuk melakukannya (mengubah nama fungsi LoadFluffiesAndScheduleNextLoad
menjadi lebih baik, tetapi itu hanya membuat masalah lebih jelas).
Apakah ada solusi elegan untuk benar-benar menulis kelas ini sesuai dengan SRP? Apakah saya terlalu berlebihan?
Atau mungkin kelas saya tidak hanya melakukan satu hal?
DateTime.UtcNow
agar Anda menghindari pergantian tabungan siang hari, atau bahkan perubahan zona waktu saat ini.Jawaban:
Jika kelas ini benar-benar sepele seperti kelihatannya, maka tidak perlu khawatir melanggar SRP. Jadi bagaimana jika fungsi 3-baris memiliki 2 baris melakukan satu hal, dan 1 baris lainnya melakukan hal lain? Ya, fungsi sepele ini melanggar SRP, dan lalu apa? Siapa peduli? Pelanggaran terhadap SRP mulai menjadi masalah ketika segalanya menjadi lebih rumit.
Masalah Anda dalam kasus khusus ini kemungkinan besar berasal dari fakta bahwa kelas lebih rumit daripada beberapa baris yang telah Anda tunjukkan kepada kami.
Secara khusus, masalah yang paling mungkin terletak pada kenyataan bahwa kelas ini tidak hanya mengelola cache, tetapi juga mungkin berisi implementasi
GetFluffiesFromDb()
metode ini. Jadi, pelanggaran SRP ada di kelas, bukan dalam beberapa metode sepele yang ditunjukkan dalam kode yang Anda posting.Jadi, inilah saran tentang bagaimana menangani semua jenis kasus yang termasuk dalam kategori umum ini, dengan bantuan Pola Penghias .
dan digunakan sebagai berikut:
Perhatikan bagaimana
CachingFluffiesProvider.GetFluffies()
tidak takut mengandung kode yang melakukan pengecekan dan pembaruan waktu, karena itu hal-hal sepele. Apa mekanisme ini lakukan adalah untuk mengatasi dan menangani SRP di tingkat desain sistem, di mana itu penting, bukan pada tingkat metode individu kecil, di mana itu tidak masalah.sumber
Kelas Anda sendiri tampaknya baik-baik saja bagi saya, tetapi Anda benar bahwa
LoadFluffies()
tidak persis apa yang diiklankan namanya. Salah satu solusi sederhana adalah mengubah nama dan memindahkan reload eksplisit dari GetFluffies, ke fungsi dengan deskripsi yang sesuai. Sesuatu sepertiterlihat bersih bagi saya (juga karena seperti yang dikatakan Patrick: ini terdiri dari fungsi patuh SRP kecil lainnya), dan terutama juga jelas yang kadang-kadang sama pentingnya.
sumber
Saya percaya kelas Anda melakukan satu hal; ini adalah data cache dengan batas waktu. LoadFluffies tampak seperti abstraksi yang tidak berguna kecuali Anda menyebutnya dari berbagai tempat. Saya pikir akan lebih baik untuk mengambil dua baris dari LoadFluffies dan menempatkannya dalam persyaratan NeedsReload di GetFluffies. Ini akan membuat implementasi GetFluffies jauh lebih jelas dan kode masih bersih, saat Anda menyusun subrutin tanggung jawab tunggal untuk mencapai tujuan tunggal, pengambilan data yang di-cache dari db. Di bawah ini adalah metode get fluffies yang diperbarui.
sumber
Nalurimu benar. Kelas Anda, meskipun kecil, terlalu banyak melakukan. Anda harus memisahkan logika cache penyegaran berjangka waktu ke dalam kelas yang sepenuhnya umum. Kemudian buat instance spesifik kelas itu untuk mengelola Fluffies, sesuatu seperti ini (tidak dikompilasi, kode kerja dibiarkan sebagai latihan untuk pembaca):
Keuntungan tambahan adalah sekarang sangat mudah untuk menguji TimedRefreshCache.
sumber
Kelas Anda baik-baik saja, SRP adalah tentang kelas bukan fungsi, seluruh kelas bertanggung jawab untuk menyediakan "Fluffies" dari "Sumber Data" sehingga Anda bebas dalam implementasi internal.
Jika Anda ingin memperluas mekanisme cahing, Anda dapat membuat kelas bertanggung jawab untuk menonton sumber data
sumber