Apakah ada gunanya menggunakan pembangun dan antarmuka cairan dengan penginisialisasi objek?

10

Di Java dan C #, Anda bisa membuat objek dengan properti yang dapat diatur pada inisialisasi dengan mendefinisikan konstruktor dengan parameter, menentukan setiap properti setelah membangun objek, atau menggunakan pola antarmuka pembangun / fluida. Namun, C # 3 memperkenalkan inisialisasi objek dan koleksi, yang berarti pola pembangun sebagian besar tidak berguna. Dalam bahasa tanpa inisialisasi, seseorang dapat mengimplementasikan builder lalu menggunakannya seperti:

Vehicle v = new Vehicle.Builder()
                    .manufacturer("Toyota")
                    .model("Camry")
                    .year(1997)
                    .colour(CarColours.Red)
                    .addSpecialFeature(new Feature.CDPlayer())
                    .addSpecialFeature(new Feature.SeatWarmer(4))
                    .build();

Sebaliknya, dalam C # seseorang dapat menulis:

var vehicle = new Vehicle {
                Manufacturer = "Toyota",
                Model = "Camry",
                Year = 1997,
                Colour = CarColours.Red,
                SpecialFeatures = new List<SpecialFeature> {
                    new Feature.CDPlayer(),
                    new Feature.SeatWarmer { Seats = 4 }
                }
              }

... menghilangkan kebutuhan akan pembangun seperti yang terlihat pada contoh sebelumnya.

Berdasarkan contoh-contoh ini, apakah pembangun masih berguna dalam C #, atau mereka telah digantikan seluruhnya oleh inisialisasi?

svbnet
sumber
are builders usefulmengapa saya terus melihat pertanyaan-pertanyaan semacam ini? Builder adalah pola desain - tentu saja, itu dapat diekspos sebagai fitur bahasa tetapi pada akhirnya itu hanya pola desain. Pola desain tidak boleh "salah". Itu mungkin atau mungkin tidak memenuhi kasus penggunaan Anda tetapi itu tidak membuat seluruh pola salah, hanya penerapannya. Ingatlah bahwa pada akhirnya pola desain memecahkan masalah tertentu - jika Anda tidak menghadapi masalah, lalu mengapa Anda mencoba menyelesaikannya?
VLAZ
@vlaz Saya tidak mengatakan pembangun salah - pada kenyataannya pertanyaan saya adalah menanyakan apakah ada kasus penggunaan untuk pembangun yang diberikan inisialisasi adalah implementasi dari kasus yang paling umum di mana Anda akan menggunakan pembangun. Jelas, orang telah menjawab bahwa pembangun masih berguna untuk mengatur bidang pribadi, yang pada gilirannya telah menjawab pertanyaan saya.
svbnet
3
@vlaz Untuk memperjelas: Saya mengatakan bahwa inisialisasi sebagian besar telah menggantikan pola antarmuka pembangun / cairan "tradisional" untuk menulis kelas pembangun bagian dalam, kemudian menulis metode rangkaian rangkaian di dalam kelas yang menciptakan contoh baru dari kelas induk. Saya tidak mengatakan bahwa inisialisasi adalah pengganti untuk pola pembangun tertentu; Saya mengatakan bahwa initialisers menyelamatkan pengembang yang harus menerapkan pola pembangun tertentu, kecuali untuk kasus penggunaan yang disebutkan dalam jawaban, yang tidak membuat pola pembangun menjadi tidak berguna.
svbnet
5
@vlaz Saya tidak mengerti kekhawatiran Anda. "Anda mengatakan bahwa pola desain tidak berguna" - tidak, Joe bertanya apakah pola desain itu berguna. Bagaimana itu pertanyaan yang buruk, salah, atau keliru? Apakah Anda pikir jawabannya jelas? Saya kira jawabannya tidak jelas; ini sepertinya pertanyaan yang bagus untuk saya.
Tanner Swett
2
@vlaz, pola singleton dan pencari lokasi salah. Pola desain ergo bisa salah.
David Arno

Jawaban:

12

Seperti yang disentuh oleh @ user248215 masalah sebenarnya adalah ketidakberdayaan. Alasan Anda akan menggunakan pembangun di C # adalah untuk mempertahankan kejujuran penginisialisasi tanpa harus mengekspos properti yang bisa diatur. Ini bukan pertanyaan tentang enkapsulasi, itulah sebabnya saya telah menulis jawaban saya sendiri. Enkapsulasi cukup ortogonal karena memohon setter tidak menyiratkan apa yang sebenarnya dilakukan setter atau mengikat Anda pada implementasinya.

Versi selanjutnya dari C #, 8.0, kemungkinan akan memperkenalkan withkata kunci yang akan memungkinkan objek yang tidak dapat diubah diinisialisasi dengan jelas dan singkat tanpa perlu menulis pembangun.

Hal menarik lain yang dapat Anda lakukan dengan pembangun, bukan inisialisasi, adalah bahwa mereka dapat menghasilkan berbagai jenis objek tergantung pada urutan metode yang disebut.

Sebagai contoh

value.Match()
    .Case((DateTime d) => Console.WriteLine($"{d: yyyy-mm-dd}"))
    .Case((double d) => Console.WriteLine(Math.Round(d, 4));
    // void

var str = value.Match()
    .Case((DateTime d) => $"{d: yyyy-mm-dd}")
    .Case((double d) => Math.Round(d, 4).ToString())
    .ResultOrDefault(string.Empty);
    // string

Hanya untuk memperjelas contoh di atas, itu adalah pustaka pencocokan pola yang menggunakan pola pembangun untuk membangun "kecocokan" dengan menentukan kasus. Kasing ditambahkan dengan memanggil Casemetode yang melewatinya fungsi. Jika valueditugaskan untuk tipe parameter fungsi itu dipanggil. Anda dapat menemukan kode sumber lengkap di GitHub dan, karena komentar XML sulit dibaca dalam teks biasa, berikut ini tautan ke dokumentasi yang dibuat oleh SandCastle (lihat bagian Keterangan )

Aluan Haddad
sumber
Saya tidak melihat bagaimana ini tidak dapat dilakukan penginisialisasi objek, menggunakan IEnumerable<Func<T, TResult>>anggota.
Caleth
@Caleth Itu sebenarnya sesuatu yang saya coba, tetapi ada beberapa masalah dengan pendekatan itu. Itu tidak memungkinkan untuk klausa bersyarat (tidak ditampilkan tetapi digunakan dan diperlihatkan dalam dokumentasi terkait) dan itu tidak memungkinkan untuk inferensi tipe TResult. Pada akhirnya tipe-inferensi banyak berhubungan dengan itu. Saya juga ingin "terlihat" seperti struktur kontrol. Juga saya tidak ingin menggunakan penginisialisasi karena itu akan memungkinkan mutasi.
Aluan Haddad
12

Penginisialisasi objek mensyaratkan bahwa properti harus dapat diakses oleh kode panggilan. Pembangun bersarang dapat mengakses anggota pribadi kelas.

Jika Anda ingin menjadikan Vehicletidak berubah (melalui membuat semua setter pribadi), maka pembuat bersarang dapat digunakan untuk mengatur variabel pribadi.

pengguna248215
sumber
0

Mereka semua melayani keperluan yang berbeda !!!

Konstruktor dapat menginisialisasi bidang yang ditandai readonlyserta anggota pribadi dan yang dilindungi. Namun, Anda agak terbatas pada apa yang dapat Anda lakukan di dalam konstruktor; Anda harus menghindari meneruskan thiske metode eksternal, misalnya, dan Anda harus menghindari memanggil anggota virtual, karena mereka dapat mengeksekusi dalam konteks kelas turunan yang belum dibangun sendiri. Selain itu, konstruktor dijamin untuk dijalankan (kecuali jika pemanggil melakukan sesuatu yang sangat tidak biasa), jadi jika Anda mengatur bidang dalam konstruktor, kode di seluruh kelas dapat mengasumsikan bahwa bidang tersebut tidak akan menjadi nol.

Inisialisasi dijalankan setelah konstruksi. Mereka hanya membiarkan Anda memanggil properti publik; bidang pribadi dan / atau hanya baca tidak dapat disetel. Dengan konvensi, tindakan menetapkan properti harus sangat terbatas, misalnya harus idempoten, dan memiliki efek samping yang terbatas.

Metode pembangun adalah metode aktual dan karenanya memungkinkan lebih dari satu argumen, dan dengan konvensi dapat memiliki efek samping termasuk pembuatan objek. Meskipun mereka tidak dapat mengatur bidang baca saja, mereka dapat melakukan hal lain. Juga, metode dapat diimplementasikan sebagai metode ekstensi (seperti hampir semua fungsi LINQ).

John Wu
sumber