Saya berpikir bahwa cara termudah untuk memahami perbedaan antara keduanya dan mengapa wadah DI jauh lebih baik daripada pelacak layanan adalah dengan memikirkan mengapa kita melakukan inversi ketergantungan pada awalnya.
Kami melakukan inversi dependensi sehingga setiap kelas secara eksplisit menyatakan dengan tepat apa yang menjadi tanggungannya untuk operasi. Kami melakukannya karena ini menciptakan kopling paling longgar yang bisa kita capai. Semakin longgar kopling, semakin mudah untuk menguji dan melakukan refactor (dan umumnya membutuhkan paling sedikit refactoring di masa depan karena kodenya lebih bersih).
Mari kita lihat kelas berikut:
public class MySpecialStringWriter
{
private readonly IOutputProvider outputProvider;
public MySpecialFormatter(IOutputProvider outputProvider)
{
this.outputProvider = outputProvider;
}
public void OutputString(string source)
{
this.outputProvider.Output("This is the string that was passed: " + source);
}
}
Di kelas ini, kami secara eksplisit menyatakan bahwa kami memerlukan IOutputProvider dan tidak ada yang lain untuk membuat kelas ini berfungsi. Ini sepenuhnya dapat diuji dan memiliki ketergantungan pada satu antarmuka. Saya dapat memindahkan kelas ini ke mana saja dalam aplikasi saya, termasuk proyek yang berbeda dan yang diperlukan hanyalah akses ke antarmuka IOutputProvider. Jika pengembang lain ingin menambahkan sesuatu yang baru ke kelas ini, yang memerlukan ketergantungan kedua, mereka harus secara eksplisit tentang apa yang mereka butuhkan dalam konstruktor.
Lihatlah kelas yang sama dengan pencari lokasi:
public class MySpecialStringWriter
{
private readonly ServiceLocator serviceLocator;
public MySpecialFormatter(ServiceLocator serviceLocator)
{
this.serviceLocator = serviceLocator;
}
public void OutputString(string source)
{
this.serviceLocator.OutputProvider.Output("This is the string that was passed: " + source);
}
}
Sekarang saya telah menambahkan locator layanan sebagai ketergantungan. Berikut adalah masalah yang langsung terlihat:
- Masalah pertama dengan ini adalah dibutuhkan lebih banyak kode untuk mencapai hasil yang sama. Lebih banyak kode buruk. Ini bukan kode yang lebih banyak tetapi masih lebih banyak.
- Masalah kedua adalah bahwa ketergantungan saya tidak lagi eksplisit . Saya masih perlu menyuntikkan sesuatu ke dalam kelas. Kecuali sekarang hal yang saya inginkan tidak eksplisit. Itu tersembunyi di properti dari hal yang saya minta. Sekarang saya perlu akses ke ServiceLocator dan IOutputProvider jika saya ingin memindahkan kelas ke majelis yang berbeda.
- Masalah ketiga adalah bahwa ketergantungan tambahan dapat diambil oleh pengembang lain yang bahkan tidak menyadari bahwa mereka mengambilnya ketika mereka menambahkan kode ke kelas.
- Akhirnya, kode ini lebih sulit untuk diuji (bahkan jika ServiceLocator adalah antarmuka) karena kita harus mengejek ServiceLocator dan IOutputProvider, bukan hanya IOutputProvider
Jadi mengapa kita tidak menjadikan locator layanan sebagai kelas statis? Mari lihat:
public class MySpecialStringWriter
{
public void OutputString(string source)
{
ServiceLocator.OutputProvider.Output("This is the string that was passed: " + source);
}
}
Ini jauh lebih sederhana, bukan?
Salah.
Katakanlah IOutputProvider diimplementasikan oleh layanan web yang berjalan sangat lama yang menulis string dalam lima belas database berbeda di seluruh dunia dan membutuhkan waktu yang sangat lama untuk diselesaikan.
Mari kita coba kelas ini. Kami membutuhkan implementasi IOutputProvider yang berbeda untuk pengujian. Bagaimana cara kita menulis tes?
Nah untuk melakukan itu kita perlu melakukan beberapa konfigurasi mewah di kelas ServiceLocator statis untuk menggunakan implementasi IOutputProvider yang berbeda ketika sedang dipanggil oleh tes. Bahkan menulis kalimat itu menyakitkan. Menerapkannya akan menyiksa dan itu akan menjadi mimpi buruk pemeliharaan . Kita seharusnya tidak perlu memodifikasi kelas khusus untuk pengujian, terutama jika kelas itu bukan kelas yang kita coba untuk menguji.
Jadi sekarang Anda pergi dengan a) tes yang menyebabkan perubahan kode mencolok di kelas ServiceLocator yang tidak terkait; atau b) tidak ada tes sama sekali. Dan Anda pergi dengan solusi yang kurang fleksibel juga.
Jadi layanan kelas locator memiliki harus disuntikkan ke konstruktor. Yang berarti bahwa kita pergi dengan masalah khusus yang disebutkan sebelumnya. Pencari layanan memerlukan lebih banyak kode, memberi tahu pengembang lain bahwa ia membutuhkan hal-hal yang tidak diperlukan, mendorong pengembang lain menulis kode yang lebih buruk dan memberi kami lebih sedikit fleksibilitas untuk bergerak maju.
Sederhananya pelacak layanan meningkatkan sambungan dalam aplikasi dan mendorong pengembang lain untuk menulis kode yang sangat berpasangan .