Gunakan Injeksi Ketergantungan Untuk Objek Data?

11

Saya hanya belajar tentang injeksi ketergantungan, dan saya terjebak pada sesuatu. Dependency Injection merekomendasikan pengiriman kelas dependen melalui konstruktor, tapi saya ingin tahu apakah ini diperlukan untuk objek data. Karena Unit-Testability adalah salah satu manfaat utama DI, akankah suatu objek data, yang hanya menyimpan data, dan tidak ada prosedur yang pernah diuji unit, membuat DI menjadi lapisan kompleksitas yang tidak perlu, atau apakah itu masih membantu dalam menunjukkan dependensi bahkan dengan objek data?

Class DO{
    DO(){
        DataObject2List = new List<DO2>();
    }

    public string Field1;
    public string Field2;
    public List<DO2> DataObject2List;
}

Class DO2{
    public DateTime Date;
    public double Value;
}
sooprise
sumber
Apa sebenarnya yang Anda maksud dengan "objek data"? Itu bukan istilah standar. Apakah Anda berbicara tentang DTO atau merujuk ke sembarang kelas tanpa metode (seperti bagian yang membosankan dari model domain)? Ada perbedaan besar di antara keduanya.
Aaronaught
Tentu, maksud saya hanya kelas tanpa metode, kelas yang hanya menyimpan data. Jika Objek Data bukan istilah yang benar untuk ini, apakah ada, atau hanya disebut kelas tanpa metode?
sooprise
@sooprise Ini pertanyaan yang bagus. Pemikiran yang bagus.
Matthew Rodatus
@ sooprise Mungkin jawaban saya salah. Di mana Anda akan meletakkan metode CRUD? Di kelas akses data terpisah yang akan mengambil Objek Data dan bertahan mereka ke dalam tabel database? Yaitu DataAccess.Create (<DataObject>)?
Matius Rodatus
1
@ Matthew, ini akan menjadi Objek Akses Data - jika itu sebenarnya yang dibicarakan OP, maka itu tidak jelas sama sekali. Implementasi modern cenderung menjauh dari pola ini, dengan mengandalkan repositori dan / atau unit kerja.
Aaronaught

Jawaban:

7

Saya ingin menyarankan klarifikasi beberapa terminologi yang Anda gunakan di sini, khususnya "ketergantungan" dan "injeksi ketergantungan".

Ketergantungan:

"Ketergantungan" biasanya adalah objek kompleks yang melakukan beberapa fungsionalitas yang mungkin perlu bergantung pada kelas lain. Beberapa contoh klasik akan menjadi logger atau pengakses database atau beberapa komponen yang memproses bagian tertentu dari logika bisnis.

Sebuah data hanya objek seperti DTO atau objek nilai tidak biasanya disebut sebagai "ketergantungan", karena mereka tidak melakukan beberapa fungsi yang dibutuhkan.

Setelah Anda melihat cara ini, apa yang Anda lakukan dalam contoh Anda ( menyusun satu DOobjek dengan daftar D02objek melalui constructor) tidak harus dianggap "dependecy injection" sama sekali. Ini hanya pengaturan properti. Terserah Anda apakah Anda menyediakannya di konstruktor atau cara lain, tetapi hanya meneruskannya melalui konstruktor tidak menjadikannya ketergantungan injeksi.

Injeksi Ketergantungan:

Jika DO2kelas Anda benar-benar menyediakan beberapa fungsionalitas tambahan yang DOdibutuhkan kelas, maka itu akan benar-benar menjadi ketergantungan. Dalam hal itu, kelas dependen DO,, harus bergantung pada antarmuka (seperti ILogger atau IDataAccessor), dan pada gilirannya bergantung pada kode panggilan untuk menyediakan antarmuka itu (dengan kata lain, untuk 'menyuntikkan' ke DOinstance).

Menyuntikkan ketergantungan sedemikian rupa membuat DOobjek lebih fleksibel, karena setiap konteks yang berbeda dapat memberikan implementasi antarmuka untuk DOobjek itu sendiri. (Pikirkan pengujian unit.)

Eric King
sumber
7

Saya akan melakukan yang terbaik untuk menghilangkan kebingungan dalam pertanyaan.

Pertama-tama, "Objek Data" bukan istilah yang bermakna. Jika satu - satunya karakteristik yang menentukan dari objek ini adalah bahwa ia tidak memiliki metode, maka seharusnya tidak ada sama sekali . Objek tanpa perilaku yang berguna harus masuk ke dalam setidaknya satu dari subkategori berikut:

  • Objek Nilai atau "catatan" tidak memiliki identitas sama sekali. Mereka harus tipe nilai , dengan semantik salinan-pada-referensi, dengan asumsi lingkungan mendukungnya. Karena ini adalah struktur tetap, VO hanya boleh menjadi tipe primitif atau urutan primitif tetap. Oleh karena itu, VO tidak boleh memiliki dependensi atau asosiasi apa pun ; konstruktor non-default akan ada hanya untuk tujuan menginisialisasi nilai, yaitu karena tidak dapat dinyatakan sebagai literal.

  • Objek Transfer Data sering keliru dikacaukan dengan objek nilai. DTO memang memiliki identitas, atau setidaknya mereka bisa . Satu-satunya tujuan DTO adalah untuk memfasilitasi aliran informasi dari satu domain ke domain lainnya. Mereka tidak pernah memiliki "ketergantungan". Mereka mungkin memiliki asosiasi (yaitu ke array atau koleksi) tetapi kebanyakan orang lebih suka membuatnya datar. Pada dasarnya, mereka analog dengan baris dalam output dari query database; mereka adalah objek sementara yang biasanya perlu dipertahankan atau diserialisasi, dan karenanya tidak dapat merujuk jenis abstrak apa pun, karena ini akan membuatnya tidak dapat digunakan.

  • Akhirnya, Data Access Objects menyediakan pembungkus atau façade ke suatu jenis database. Ini jelas memiliki dependensi - mereka tergantung pada koneksi database dan / atau komponen ketekunan. Namun, ketergantungan mereka hampir selalu dikelola secara eksternal dan sama sekali tidak terlihat oleh penelepon. Dalam pola Rekaman Aktif itu adalah kerangka kerja yang mengelola semuanya melalui konfigurasi; dalam model DAO yang lebih tua (kuno menurut standar saat ini) Anda hanya bisa membuatnya melalui wadah. Jika saya melihat salah satunya dengan injeksi konstruktor, saya akan sangat, sangat khawatir.

Anda juga mungkin berpikir dari objek entitas atau "objek bisnis" , dan dalam hal ini Anda lakukan ingin dukungan injeksi ketergantungan, tapi tidak dalam cara yang menurut Anda atau untuk alasan yang Anda pikirkan. Ini bukan untuk kepentingan kode pengguna , itu untuk kepentingan manajer entitas atau ORM, yang akan secara diam-diam menyuntikkan proksi yang disadap untuk melakukan hal-hal mewah seperti pemahaman kueri atau pemuatan malas.

Dalam hal ini, Anda biasanya tidak menyediakan konstruktor untuk injeksi; alih-alih, Anda hanya perlu membuat properti virtual dan menggunakan tipe abstrak (mis. IList<T>alih-alih List<T>). Sisanya terjadi di belakang layar, dan tidak ada yang lebih bijak.

Jadi, secara keseluruhan, saya akan mengatakan bahwa pola DI yang terlihat sedang diterapkan ke "objek data" tidak diperlukan dan bahkan mungkin bendera merah; tetapi sebagian besar, itu karena keberadaan objek adalah bendera merah, kecuali dalam kasus ketika objek tersebut secara khusus digunakan untuk mewakili data dari database. Dalam hampir setiap kasus lain itu adalah bau kode, biasanya awal dari Model Domain Anemik atau setidaknya Poltergeist .

Untuk mengulangi:

  1. Jangan membuat "objek data".
  2. Jika Anda harus membuat "objek data", maka pastikan ia memiliki tujuan yang jelas . Itu tujuan akan memberitahu Anda apakah atau tidak DI tepat. Tidak mungkin membuat keputusan desain yang berarti tentang objek yang tidak seharusnya ada di tempat pertama.

Sirip.

Aaronaught
sumber
0

Dalam contoh Anda, DOtidak memiliki dependensi fungsional (pada dasarnya karena tidak melakukan apa-apa). Itu memang memiliki ketergantungan pada jenis beton DO2, jadi Anda mungkin ingin memperkenalkan antarmuka ke abstrak DO2, sehingga konsumen dapat mengimplementasikan implementasi konkret mereka sendiri dari kelas anak.

Sungguh, ketergantungan apa yang akan Anda suntikkan di sini?

Scott Whitlock
sumber
Per pertanyaan lain tentang dia yang saya jawab, saya pikir pertanyaan itu mengacu pada Obyek Data dengan operasi CRUD yang tergabung. Bagaimana / di mana ketergantungan basis data disuntikkan ke dalam Obyek Data? Dalam metodenya? Di konstruktor? Cara lain? Asumsinya adalah bahwa dependensi tidak boleh disembunyikan dalam tubuh metode - yang menonaktifkan memisahkan ketergantungan database dari Obyek Data sehingga Objek Data dapat diuji unit.
Matthew Rodatus
@ Matthew Rodatus - Begitu ya. Ya, dalam hal itu, Anda memiliki dua pilihan: menyuntikkan layanan ketekunan atau membuat kelas lain yang disebut DOPersisteryang tahu cara bertahan DOdan meninggalkannya sebagai objek hanya data semata (lebih baik menurut saya). Dalam kasus terakhir, DOPersisterakan disuntikkan dengan ketergantungan database.
Scott Whitlock
Setelah membaca kembali pertanyaannya, saya kurang yakin. Analisis saya mungkin salah. Dia mengatakan dalam pertanyaannya bahwa DO-nya tidak akan memiliki prosedur. Itu berarti kegigihan TIDAK terjadi dalam DO. Dalam hal ini, jawaban Anda benar - tidak ada ketergantungan untuk disuntikkan.
Matius Rodatus
0

Karena ini adalah Objek Data di lapisan akses data, itu harus bergantung langsung pada layanan database. Anda bisa menentukan Layanan Database untuk konstruktor:

DataObject dataObject = new DataObject(new DatabaseService());
dataObject.Update();

Tapi, injeksi tidak harus di dalam konstruktor. Atau, Anda bisa memberikan ketergantungan melalui setiap metode CRUD. Saya lebih suka metode ini daripada yang sebelumnya karena Objek Data Anda tidak perlu tahu di mana itu akan bertahan sampai Anda benar-benar perlu bertahan.

DataObject dataObject = new DataObject();
dataObject.Update(new DatabaseService());

Anda tentu tidak ingin menyembunyikan konstruksi dengan metode CRUD!

public void Update()
{
    // DON'T DO THIS!
    using (DatabaseService dbService = new DatabaseService())
    {
        ...
    }
}

Opsi alternatif adalah membangun DatabaseService melalui metode kelas yang dapat ditimpa.

public void Update()
{
    // GetDatabaseService() is protected virtual, so in unit testing
    // you can subclass the Data Object and return your own
    // MockDatabaseService.
    using (DatabaseService dbService = GetDatabaseService())
    {
        ...
    }
}

Alternatif terakhir adalah menggunakan ServiceLocator gaya tunggal. Meskipun saya tidak suka opsi ini, itu bisa diuji unit.

public void Update()
{
    // The ServiceLocator would not be a real singleton. It would have a setter
    // property so that unit tests can swap it out with a mock implementation
    // for unit tests.
    using (DatabaseService dbService = ServiceLocator.GetDatabaseService())
    {
        ...
    }
}
Matthew Rodatus
sumber