Dalam artikel saya sebelumnya, saya menentang injeksi penyetel. Ketergantungan opsional adalah salah satu keberatan utama yang diajukan dalam komentar dan di tempat lain. Saya memang menyebutkan ini dan menyarankan menjadikannya parameter konstruktor opsional. Saya juga menyebutkan bahwa refactoring agar tidak lagi opsional adalah solusinya.
Saya pikir ini perlu dieksplorasi lebih lanjut. Saya pikir kerugian dari injeksi penyetel berarti harus dihindari. Menurut saya keuntungan menangani dependensi opsional lebih besar daripada kerugiannya. Tapi itu bukanlah pandangan yang disetujui semua orang.
Melihat cara saya menangani dependensi opsional membuat saya menyadari bahwa saya tidak hanya menghindari injeksi penyetel. Saya juga biasanya menghindari ketergantungan opsional. Setelah berpikir lebih jauh, sekarang saya akan menghindari penggunaannya sepenuhnya.
Jadi mengapa ketergantungan opsional menjadi masalah? Biasanya, dependensi opsional menunjukkan bahwa kategori tersebut memiliki beberapa perilaku. Oleh karena itu mempunyai tanggung jawab ganda dan tidak menganut prinsip tanggung jawab tunggal. Kategori dengan dependensi opsional selalu hanya melakukan satu hal. Terkadang ia melakukan hal lain jika ada dependensi opsional.
Sebaiknya kita mengekstrak perilaku opsional ke dalam kategori lain. Kelas baru ini dapat memiliki tanggung jawab penuh atas tindakan tersebut. Di kategori baru ini bukan opsional, sekarang kita bisa memilih versi yang mana. Kami sekarang memutuskan apakah kami hanya memerlukan perilaku opsional dalam konfigurasi, bukan perilaku opsional dalam konfigurasi dan objek.
Sekalipun hal ini tidak memungkinkan, sebaiknya kita menghindari ketergantungan opsional. Memperlakukan logging sebagai perilaku opsional adalah contoh tandingan yang umum. Jadi kategori dengan logging opsional mungkin terlihat seperti ini:
private $otherDependency;
private $logger;
public function __construct(OtherDependency $otherDependency, LoggerInterface $logger = null)
{
$this->otherDependency = $otherDependency;
$this->logger = $logger;
}
public function doSomething()
{
//do something
if ($this->logger) {
$this->logger->info('something to log');
}
}
Ada masalah lain di sini, dan itu adalah kita harus membungkus setiap penggunaan logger sebagai kondisional. Jika kita tidak melakukan ini, kita akan mendapatkan kesalahan fatal saat memanggil metode logger yang tidak ada. Hal ini meningkatkan kompleksitas kategori dan meningkatkan risiko kesalahan. Kesalahan ini mudah dihindari melalui spesifikasi atau pengujian unit. Namun, kita bahkan dapat menghindari melakukan hal ini. Akan lebih mudah jika kategori kita terlihat seperti ini:
private $otherDependency;
private $logger;
public function __construct(OtherDependency $otherDependency, LoggerInterface $logger)
{
$this->otherDependency = $otherDependency;
$this->logger = $logger;
}
public function doSomething()
{
//do something
$this->logger->info('something to log');
}
Kita dapat mencapai hal ini dengan menggunakan objek Null dan tetap mencapai perilaku logging opsional yang diinginkan. Kita bisa memasukkan logger kosong yang mengimplementasikan antarmuka logger, namun ia tidak melakukan apa pun saat kita tidak ingin login. Kelas kita tidak perlu mengetahui hal ini, ia akan menggunakan apa pun yang memiliki antarmuka. Faktanya, jika Anda menggunakan logger PSR3, ada implementasi null logger di psr/log suite.
Ya, kami memerlukan kelas tambahan, tetapi kami dapat menemukan logging opsional di banyak kelas. Kita dapat menghapus kondisi dari banyak tempat di kode kita demi kelas tambahan. Hal ini mengurangi jumlah potensi kesalahan dan membuat kode lebih mudah dibaca. Menghapus sejumlah besar kondisi sebagai ganti pembuatan satu kategori adalah pemfaktoran ulang kode yang baik.
Menghidupkan dan mematikan login pada konfigurasi tidaklah rumit. Ketika kita tidak membutuhkan logging, layanan logger bisa saja menjadi logger kosong. Kami akan mematikan logging tanpa melakukan perubahan apa pun.
Kami sekarang memiliki kode yang lebih sederhana. Kami dapat menghapus semua ketentuan. Kami menghindari injeksi penyetel. Semua ini tanpa menambahkan apa pun yang lebih kompleks pada kode kita selain implementasi null sederhana dari logger.
Pemikiran saya adalah bahwa ketergantungan opsional adalah bau kode itu sendiri. Suntikan Setter tidak menghilangkan bau. Menghentikan ketergantungan adalah opsional, lebih penting daripada cara kita menyuntikkannya.