Saya memiliki fungsi API ini:
public ResultEnum DoSomeAction(string a, string b, DateTime c, OtherEnum d,
string e, string f, out Guid code)
Saya tidak suka itu. Karena urutan parameter menjadi tidak penting. Semakin sulit untuk menambahkan bidang baru. Lebih sulit untuk melihat apa yang sedang diedarkan. Lebih sulit untuk mengubah metode menjadi bagian-bagian yang lebih kecil karena ini menciptakan overhead lain untuk melewatkan semua parameter dalam sub fungsi. Kode lebih sulit dibaca.
Saya datang dengan ide yang paling jelas: memiliki objek merangkum data dan menyebarkannya alih-alih melewati setiap parameter satu per satu. Inilah yang saya pikirkan:
public class DoSomeActionParameters
{
public string A;
public string B;
public DateTime C;
public OtherEnum D;
public string E;
public string F;
}
Itu mengurangi deklarasi API saya ke:
public ResultEnum DoSomeAction(DoSomeActionParameters parameters, out Guid code)
Bagus. Terlihat sangat polos tapi kami benar-benar memperkenalkan perubahan besar: kami memperkenalkan kemampuan berubah-ubah. Karena apa yang sebelumnya telah kami lakukan sebenarnya untuk melewatkan objek abadi anonim: parameter fungsi pada stack. Sekarang kami membuat kelas baru yang sangat bisa berubah. Kami menciptakan kemampuan untuk memanipulasi keadaan penelepon . Itu menyebalkan. Sekarang saya ingin objek saya tidak berubah, apa yang harus saya lakukan?
public class DoSomeActionParameters
{
public string A { get; private set; }
public string B { get; private set; }
public DateTime C { get; private set; }
public OtherEnum D { get; private set; }
public string E { get; private set; }
public string F { get; private set; }
public DoSomeActionParameters(string a, string b, DateTime c, OtherEnum d,
string e, string f)
{
this.A = a;
this.B = b;
// ... tears erased the text here
}
}
Seperti yang Anda lihat, saya benar-benar menciptakan kembali masalah asli saya: terlalu banyak parameter. Sudah jelas bahwa itu bukan cara untuk pergi. Apa yang akan aku lakukan? Opsi terakhir untuk mencapai ketidakberubahan tersebut adalah dengan menggunakan struct "readonly" seperti ini:
public struct DoSomeActionParameters
{
public readonly string A;
public readonly string B;
public readonly DateTime C;
public readonly OtherEnum D;
public readonly string E;
public readonly string F;
}
Itu memungkinkan kita untuk menghindari konstruktor dengan terlalu banyak parameter dan mencapai kekekalan. Sebenarnya itu memperbaiki semua masalah (pemesanan parameter dll). Namun:
- Semua orang (termasuk FXCop & Jon Skeet) setuju bahwa mengekspos bidang publik adalah buruk .
- Eric Lippert dkk mengatakan mengandalkan bidang yang hanya bisa dibaca untuk kekekalan adalah dusta .
Saat itulah saya menjadi bingung dan memutuskan untuk menulis pertanyaan ini: Apa cara paling mudah dalam C # untuk menghindari masalah "terlalu banyak parameter" tanpa memperkenalkan kemampuan berubah-ubah? Apakah mungkin untuk menggunakan struct hanya baca untuk tujuan itu dan belum memiliki desain API yang buruk?
KLARIFIKASI:
- Harap asumsikan tidak ada pelanggaran terhadap prinsip tanggung jawab tunggal. Dalam kasus asli saya fungsi hanya menulis parameter yang diberikan ke catatan DB tunggal.
- Saya tidak mencari solusi spesifik untuk fungsi yang diberikan. Saya mencari pendekatan umum untuk masalah seperti itu. Saya secara khusus tertarik untuk memecahkan masalah "terlalu banyak parameter" tanpa memperkenalkan kemampuan berubah-ubah atau desain yang mengerikan.
MEMPERBARUI
Jawaban yang diberikan di sini memiliki kelebihan / kekurangan yang berbeda. Karenanya saya ingin mengonversikan ini ke wiki komunitas. Saya pikir setiap jawaban dengan contoh kode dan Pro / Kontra akan menjadi panduan yang baik untuk masalah yang sama di masa depan. Saya sekarang mencoba mencari tahu bagaimana melakukannya.
DoSomeActionParameters
adalah objek sekali pakai, yang akan dibuang setelah pemanggilan metode.Jawaban:
Satu gaya yang dianut dalam kerangka kerja biasanya seperti pengelompokan parameter terkait ke dalam kelas terkait (tapi sekali lagi bermasalah dengan mutabilitas):
sumber
Gunakan kombinasi API gaya builder dan bahasa khusus domain - Antarmuka Lancar. API sedikit lebih verbose tetapi dengan intellisense sangat cepat untuk mengetik dan mudah dimengerti.
sumber
DoSomeAction
merupakan metode untuk itu.Apa yang Anda miliki di sana adalah indikasi yang cukup yakin bahwa kelas tersebut melanggar Prinsip Tanggung Jawab Tunggal karena memiliki terlalu banyak ketergantungan. Cari cara untuk memperbaiki ketergantungan tersebut ke dalam kelompok Dependensi Fasad .
sumber
Cukup ubah struktur data parameter Anda dari a
class
kestruct
dan Anda siap melakukannya.Metode sekarang akan mendapatkan salinan strukturnya sendiri. Perubahan yang dilakukan pada variabel argumen tidak dapat diamati oleh metode, dan perubahan metode membuat ke variabel tidak dapat diamati oleh pemanggil. Isolasi dicapai tanpa kekekalan.
Pro:
Cons:
sumber
Bagaimana dengan membuat kelas builder di dalam kelas data Anda. Kelas data akan memiliki semua setter sebagai pribadi dan hanya pembangun yang dapat mengaturnya.
sumber
DoSomeActionParameters.Builder
instance dapat digunakan untuk membuat dan mengkonfigurasi tepat satuDoSomeActionParameters
instance. Setelah memanggilBuild()
perubahan selanjutnya padaBuilder
properti akan terus memodifikasi properti instance asliDoSomeActionParameters
, dan panggilan selanjutnya untukBuild()
akan terus mengembalikanDoSomeActionParameters
instance yang sama . Seharusnya memang begitupublic DoSomeActionParameters Build() { var oldObj = obj; obj = new DoSomeActionParameters(); return oldObj; }
.Saya bukan programmer C # tetapi saya percaya C # mendukung argumen bernama: (F # tidak dan C # sebagian besar fitur yang kompatibel untuk hal semacam itu) Itu: http://msdn.microsoft.com/en-us/library/dd264739 .aspx # Y342
Jadi memanggil kode asli Anda menjadi:
ini tidak memerlukan lebih banyak ruang / pemikiran bahwa pembuatan objek Anda dan memiliki semua manfaat, dari fakta bahwa Anda belum mengubah apa yang terjadi dalam sistem yang sama sekali tidak ada. Anda bahkan tidak perlu melakukan pengkodean ulang apa pun untuk menunjukkan argumen yang disebutkan
Sunting: di sini ada yang artis yang saya temukan tentang itu. http://www.globalnerdy.com/2009/03/12/default-and-named-parameters-in-c-40-sith-lord-in-training/ Saya harus menyebutkan C # 4.0 mendukung argumen bernama, 3.0 tidak
sumber
Mengapa tidak membuat antarmuka yang menegakkan imutabilitas (yaitu hanya getter)?
Ini pada dasarnya solusi pertama Anda, tetapi Anda memaksa fungsi untuk menggunakan antarmuka untuk mengakses parameter.
dan deklarasi fungsi menjadi:
Pro:
struct
solusiCons:
DoSomeActionParameters
adalah kelas yang bisa dipetakanIDoSomeActionParameters
sumber
IDoSomeActionParameters
dan ingin menangkap nilai-nilai di dalamnya tidak memiliki cara untuk mengetahui apakah memegang referensi akan cukup, atau jika harus menyalin nilai-nilai ke objek lain. Antarmuka yang dapat dibaca bermanfaat dalam beberapa konteks, tetapi bukan sebagai alat untuk membuat hal-hal yang tidak berubah.Saya tahu ini adalah pertanyaan lama tapi saya pikir saya akan mengemukakan saran saya karena saya baru saja menyelesaikan masalah yang sama. Sekarang, dapat diterima masalah saya sedikit berbeda dengan Anda karena saya memiliki persyaratan tambahan karena tidak ingin pengguna dapat membuat objek ini sendiri (semua hidrasi data berasal dari database, sehingga saya dapat mem-jailbreak semua konstruksi secara internal). Ini memungkinkan saya untuk menggunakan konstruktor pribadi dan pola berikut;
sumber
Anda bisa menggunakan pendekatan gaya Builder, meskipun tergantung pada kompleksitas
DoSomeAction
metode Anda , ini mungkin merupakan sentuhan kelas berat. Sesuatu di sepanjang garis ini:sumber
Selain respons manji - Anda mungkin juga ingin membagi satu operasi menjadi beberapa yang lebih kecil. Membandingkan:
dan
Bagi mereka yang tidak tahu POSIX penciptaan anak bisa semudah:
Setiap pilihan desain mewakili trade-off atas operasi apa yang mungkin dilakukan.
PS. Ya - mirip dengan builder - hanya secara terbalik (yaitu di sisi callee dan bukan penelepon). Mungkin atau mungkin tidak lebih baik daripada pembangun dalam kasus khusus ini.
sumber
di sini ada yang sedikit berbeda dari Mikeys tetapi apa yang saya coba lakukan adalah membuat semuanya sesedikit mungkin untuk menulis
DoSomeActionParameters tidak dapat diubah karena dapat dan tidak dapat dibuat secara langsung karena konstruktor defaultnya adalah pribadi
Penginisialisasi tidak berubah, tetapi hanya transportasi
Penggunaan mengambil keuntungan dari initializer pada Initializer (jika Anda mengerti maksud saya) Dan saya dapat memiliki default di konstruktor default Initializer
Parameter akan menjadi opsional di sini, jika Anda ingin beberapa diperlukan Anda bisa meletakkannya di konstruktor default Initializer
Dan validasi bisa masuk dalam metode Buat
Jadi sekarang sepertinya
Masih sedikit kooki yang saya tahu, tetapi saya akan tetap mencobanya
Sunting: memindahkan metode buat ke statis di objek parameter dan menambahkan delegasi yang melewati initializer mengambil beberapa kookieness keluar dari panggilan
Jadi panggilannya sekarang terlihat seperti ini
sumber
Gunakan struktur, tetapi alih-alih bidang publik, memiliki properti publik:
Jon dan FXCop akan puas karena Anda mengekspos pantasnya bukan bidang.
Eric akan puas karena menggunakan properti, Anda dapat memastikan bahwa nilainya hanya ditetapkan sekali.
Satu objek semi-permanen (nilai dapat diatur tetapi tidak diubah). Bekerja untuk nilai dan tipe Referensi.
sumber
this
jauh lebih jahat daripada bidang publik. Saya tidak yakin mengapa Anda berpikir hanya menetapkan nilai sekali membuat sesuatu "oke"; jika seseorang memulai dengan instance default dari struct, masalah yang terkait denganthis
properti -mutating masih ada.Varian dari jawaban Samuel yang saya gunakan dalam proyek saya ketika saya memiliki masalah yang sama:
Dan untuk menggunakan:
Dalam kasus saya, parameter sengaja dimodifikasi, karena metode setter tidak memungkinkan untuk semua kemungkinan kombinasi, dan hanya mengekspos kombinasi umum dari mereka. Itu karena beberapa parameter saya cukup kompleks dan metode penulisan untuk semua kasus yang mungkin akan menjadi sulit dan tidak perlu (kombinasi gila jarang digunakan).
sumber
DoMagic
telah di kontrak meskipun itu tidak akan mengubah objek. Tapi saya rasa itu tidak melindungi dari modifikasi yang tidak disengaja.