Beralih atau Kamus saat menetapkan ke objek baru

12

Baru-baru ini, saya lebih suka memetakan 1-1 hubungan menggunakan Dictionariesdaripada Switchpernyataan. Saya merasa sedikit lebih cepat untuk menulis dan proses mental lebih mudah. Sayangnya, saat memetakan ke instance objek baru, saya tidak ingin mendefinisikannya seperti ini:

var fooDict = new Dictionary<int, IBigObject>()
{
    { 0, new Foo() }, // Creates an instance of Foo
    { 1, new Bar() }, // Creates an instance of Bar
    { 2, new Baz() }  // Creates an instance of Baz
}

var quux = fooDict[0]; // quux references Foo

Mengingat konstruk itu, saya telah membuang siklus CPU dan memori membuat 3 objek, melakukan apa pun yang mungkin mengandung konstruktor mereka, dan akhirnya hanya menggunakan salah satu dari mereka. Saya juga percaya bahwa memetakan objek lain fooDict[0]dalam hal ini akan menyebabkan mereka merujuk hal yang sama, daripada membuat contoh baru Fooseperti yang dimaksudkan. Solusinya adalah dengan menggunakan lambda sebagai gantinya:

var fooDict = new Dictionary<int, Func<IBigObject>>()
{
    { 0, () => new Foo() }, // Returns a new instance of Foo when invoked
    { 1, () => new Bar() }, // Ditto Bar
    { 2, () => new Baz() }  // Ditto Baz
}

var quux = fooDict[0](); // equivalent to saying 'var quux = new Foo();'

Apakah ini sampai pada titik di mana itu terlalu membingungkan? Sangat mudah untuk melewatkannya ()pada akhirnya. Atau apakah pemetaan ke fungsi / ekspresi merupakan praktik yang cukup umum? Alternatifnya adalah menggunakan sakelar:

IBigObject quux;
switch(someInt)
{
    case 0: quux = new Foo(); break;
    case 1: quux = new Bar(); break;
    case 2: quux = new Baz(); break;
}

Doa mana yang lebih bisa diterima?

  • Kamus, untuk pencarian lebih cepat dan lebih sedikit kata kunci (huruf besar dan kecil)
  • Switch: Lebih umum ditemukan dalam kode, tidak memerlukan penggunaan fungsi <> objek untuk tipuan.
KChaloux
sumber
2
tanpa lambda Anda akan memiliki instance yang sama dikembalikan setiap kali Anda melakukan pencarian dengan kunci yang sama (seperti pada fooDict[0] is fooDict[0]). dengan kedua lambda dan saklar ini tidak terjadi
ratchet freak
@ scratchetfreak Ya, saya benar-benar menyadari hal ini ketika saya mengetikkan contoh. Saya pikir saya membuat catatan di suatu tempat.
KChaloux
1
Saya kira kenyataan bahwa Anda secara eksplisit memasukkannya ke dalam Konstanta berarti bahwa Anda memerlukan objek yang dibuat untuk bisa berubah. Tetapi jika suatu hari Anda dapat membuatnya tidak berubah, maka mengembalikan objek secara langsung akan menjadi solusi terbaik. Anda dapat menempatkan dict di bidang const dan hanya dikenakan biaya pembuatan sekali dalam seluruh aplikasi.
Laurent Bourgault-Roy

Jawaban:

7

Itu hal yang menarik dari pola pabrik . Saya suka kombinasi Kamus dan ekspresi lambda; itu membuat saya melihat wadah itu dengan cara yang baru.

Saya mengabaikan kekhawatiran dalam pertanyaan Anda tentang siklus CPU seperti yang Anda sebutkan di komentar bahwa pendekatan non-lambda tidak memberikan apa yang Anda butuhkan.

Saya pikir salah satu pendekatan (beralih vs. Kamus + lambda) akan baik-baik saja. Satu-satunya batasan adalah bahwa dengan menggunakan Kamus, Anda membatasi jenis input yang dapat Anda terima untuk menghasilkan kelas yang dikembalikan.

Menggunakan pernyataan switch akan memberi Anda lebih banyak fleksibilitas pada parameter input. Namun, jika ini menjadi masalah, Anda bisa membungkus Kamus di dalam metode dan memiliki hasil akhir yang sama.

Jika ini baru untuk tim Anda, komentari kodenya dan jelaskan apa yang terjadi. Meminta peninjauan kode tim, menuntun mereka melalui apa yang telah dilakukan dan membuat mereka menyadarinya. Selain itu, terlihat baik-baik saja.


sumber
Sayangnya, sekitar sebulan yang lalu, tim saya hanya terdiri dari saya (pemimpin berhenti). Saya tidak memikirkan relevansinya dengan pola pabrik. Sebenarnya itu pengamatan yang rapi.
KChaloux
1
@KChaloux: Tentu saja, jika Anda hanya menggunakan pola Metode Pabrik, Anda case 0: quux = new Foo(); break;menjadi case 0: return new Foo();yang sejujurnya mudah ditulis dan jauh lebih mudah dibaca daripada{ 0, () => new Foo() }
pdr
@ pdr Sudah muncul beberapa tempat dalam kode. Mungkin ada alasan bagus untuk membuat metode pabrik pada objek yang menginspirasi pertanyaan ini, tapi saya pikir itu cukup menarik untuk ditanyakan sendiri.
KChaloux
1
@KChaloux: Saya akui saya tidak tertarik pada obsesi baru-baru ini dengan mengganti saklar / case dengan kamus. Saya belum melihat kasus di mana menyederhanakan dan mengisolasi saklar dengan metode sendiri tidak akan lebih efektif.
pdr
@ pdr Obsession adalah kata yang kuat untuk digunakan di sini. Lebih banyak pertimbangan ketika memutuskan bagaimana menangani pemetaan satu kali antara nilai. Saya setuju bahwa dalam kasus di mana itu diulang, yang terbaik untuk mengisolasi metode penciptaan.
KChaloux
7

C # 4.0 memberi Anda Lazy<T>kelas, yang mirip dengan solusi kedua Anda sendiri, tetapi berteriak "inisialisasi malas" lebih eksplisit.

var fooDict = new Dictionary<int, Lazy<IBigObject>>()
{
    { 0, new Lazy(() => new Foo()) }, // Returns a new instance of Foo when invoked
    { 1, new Lazy(() => new Bar()) }, // Ditto Bar
    { 2, new Lazy(() => new Baz()) }  // Ditto Baz
}
Avner Shahar-Kashtan
sumber
Ooh, saya tidak tahu itu.
KChaloux
Oh itu bagus!
Laurent Bourgault-Roy
2
Namun, begitu Lazy.Value dipanggil, ia menggunakan contoh yang sama untuk seumur hidup. Lihat Inisialisasi Lazy
Dan Lyons
Tentu saja, kalau tidak inisialisasi malas, hanya inisialisasi ulang setiap waktu.
Avner Shahar-Kashtan
OP menyatakan bahwa ia membutuhkannya untuk membuat instance baru setiap kali. Solusi kedua dengan lambdas dan solusi ketiga dengan switch keduanya melakukan itu, sedangkan solusi pertama dan implementasi <T> Malas tidak.
Dan Lyons
2

Secara gaya saya pikir keterbacaan sama di antara mereka. Lebih mudah untuk melakukan injeksi ketergantungan dengan Dictionary.

Jangan lupa bahwa Anda harus memeriksa apakah kunci ada saat menggunakan Dictionary, dan harus memberikan cadangan jika tidak.

Saya lebih suka switchpernyataan untuk jalur kode statis, dan Dictionaryuntuk jalur kode dinamis (di mana Anda dapat menambah atau menghapus entri). Kompiler mungkin dapat melakukan beberapa optimasi statis dengan switchyang tidak dapat dilakukan dengan Dictionary.

Menariknya, Dictionarypola ini adalah apa yang kadang-kadang dilakukan orang dengan Python, karena Python tidak memiliki switchpernyataan. Kalau tidak, mereka menggunakan rantai if-else.

M. Dudley
sumber
1

Secara umum, saya tidak akan memilih keduanya.

Apa pun yang dikonsumsi ini harus bekerja dengan a Func<int, IBigObject>. Kemudian sumber pemetaan Anda dapat berupa Kamus atau metode yang memiliki pernyataan beralih atau panggilan layanan web atau beberapa pencarian file ... apa pun.

Sedangkan untuk implementasi, saya lebih suka Kamus karena itu lebih mudah dire-refored dari 'kamus kode keras, kunci pencarian, hasil kembali' untuk 'memuat kamus dari file, kunci pencarian, hasil kembali'.

Telastyn
sumber