Kami melakukan banyak pengujian unit dan refactoring objek bisnis kami, dan saya tampaknya memiliki pendapat yang sangat berbeda tentang desain kelas daripada rekan-rekan lainnya.
Contoh kelas yang saya bukan penggemar:
public class Foo
{
private string field1;
private string field2;
private string field3;
private string field4;
private string field5;
public Foo() { }
public Foo(string in1, string in2)
{
field1 = in1;
field2 = in2;
}
public Foo(string in1, string in2, string in3, string in4)
{
field1 = in1;
field2 = in2;
field3 = in3;
}
public Prop1
{ get { return field1; } }
{ set { field1 = value; } }
public Prop2
{ get { return field2; } }
{ set { field2 = value; } }
public Prop3
{ get { return field3; } }
{ set { field3 = value; } }
public Prop4
{ get { return field4; } }
{ set { field4 = value; } }
public Prop5
{ get { return field5; } }
{ set { field5 = value; } }
}
Di kelas "nyata", mereka tidak semua string, tetapi dalam beberapa kasus kami memiliki 30 bidang dukungan untuk properti publik sepenuhnya.
Saya benci kelas ini, dan saya tidak tahu apakah saya hanya pilih-pilih. Beberapa hal yang perlu diperhatikan:
- Bidang dukungan pribadi tanpa logika di properti, tampaknya tidak perlu dan membengkak kelas
- Banyak konstruktor (agak ok) tetapi ditambah dengan
- semua properti memiliki setter publik, saya bukan penggemar.
- Secara potensial tidak ada properti yang akan diberi nilai karena konstruktor kosong, jika penelepon tidak mengetahui, Anda berpotensi mendapatkan beberapa yang sangat tidak diinginkan dan sulit untuk diuji perilaku.
- Terlalu banyak properti! (dalam 30 kasus)
Saya merasa jauh lebih sulit untuk benar - benar mengetahui keadaan objek Foo
saat itu, sebagai pelaksana. Argumen dibuat "kita mungkin tidak memiliki informasi yang diperlukan untuk ditetapkan Prop5
pada saat pembangunan objek. Ok, saya kira saya bisa mengerti itu, tetapi jika itu hanya membuat Prop5
setter publik, tidak selalu hingga 30 properti di kelas.
Apakah saya hanya menjadi pemilih dan / atau gila karena menginginkan kelas yang "mudah digunakan" sebagai lawan dari "mudah untuk menulis (semuanya publik)"? Kelas-kelas seperti berteriak di atas kepada saya, saya tidak tahu bagaimana ini akan digunakan, jadi saya hanya akan membuat semuanya publik untuk berjaga-jaga .
Jika saya tidak terlalu pilih-pilih, argumen apa yang bagus untuk memerangi pemikiran seperti ini? Saya tidak pandai mengartikulasikan argumen, karena saya menjadi sangat frustrasi ketika mencoba menyampaikan pendapat saya (tentu saja tidak sengaja).
sumber
get
atauset
:-)Jawaban:
Kelas yang sepenuhnya publik memiliki pembenaran untuk situasi tertentu, dan juga yang ekstrem lainnya, kelas dengan hanya satu metode publik (dan mungkin banyak metode pribadi). Dan kelas dengan beberapa metode publik, beberapa pribadi juga.
Itu semua tergantung pada jenis abstraksi yang akan Anda modelkan dengan mereka, lapisan mana yang Anda miliki dalam sistem Anda, tingkat enkapsulasi yang Anda butuhkan di lapisan yang berbeda, dan (tentu saja) aliran pemikiran mana yang berasal dari kelas penulis dari. Anda dapat menemukan semua jenis ini dalam kode SOLID.
Ada seluruh buku yang ditulis tentang kapan harus memilih jenis desain, jadi saya tidak akan mencantumkan aturan apa pun di sini tentang hal itu, ruang di bagian ini tidak akan cukup. Namun, jika Anda memiliki contoh dunia nyata untuk abstraksi yang ingin Anda modelkan dengan sebuah kelas, saya yakin komunitas di sini dengan senang hati akan membantu Anda untuk meningkatkan desain.
Untuk membahas poin Anda yang lain:
Jadi, bukannya
menulis
atau
"Beberapa konstruktor": itu bukan masalah. Itu mendapat masalah ketika ada duplikasi kode yang tidak perlu di sana, seperti ditunjukkan dalam contoh Anda, atau hirarki panggilan berbelit-belit. Ini dapat dengan mudah diselesaikan dengan refactoring bagian-bagian umum menjadi fungsi yang terpisah, dan dengan mengatur rantai konstruktor secara searah
"Secara potensial tidak ada properti yang akan diberi nilai karena konstruktor kosong": dalam C #, setiap tipe data memiliki nilai default yang jelas. Jika properti tidak diinisialisasi secara eksplisit dalam konstruktor, mereka mendapatkan nilai default ini ditetapkan. Jika ini digunakan dengan sengaja , itu benar-benar oke - jadi konstruktor kosong mungkin ok jika penulis tahu apa yang dia lakukan.
"Terlalu banyak properti! (Dalam 30 kasus)": ya, jika Anda bebas merancang kelas seperti itu dengan cara greenfield, 30 terlalu banyak, saya setuju. Namun, tidak semua dari kita memiliki kemewahan ini (bukankah Anda menulis di komentar di bawah ini adalah sistem warisan?). Terkadang Anda harus memetakan catatan dari database yang ada, atau file, atau data dari API pihak ketiga ke sistem Anda. Jadi untuk kasus-kasus ini, 30 atribut mungkin sesuatu yang harus dijalani seseorang.
sumber
null
bukan? jadi jika salah satu properti yang sebenarnya digunakan dalam.ToUpper()
, tetapi nilai tidak pernah ditetapkan untuk itu, itu akan membuang pengecualian runtime. Ini adalah contoh yang baik mengapa diperlukan data untuk kelas harus memiliki harus ditetapkan selama konstruksi objek. Tidak hanya menyerahkannya kepada pengguna. Terima kasihDengan mengatakan bahwa "Bidang dukungan pribadi tanpa logika di properti, tampaknya tidak perlu dan membesar-besarkan kelas" Anda sudah memiliki argumen yang layak.
Sepertinya bukan beberapa konstruktor yang menjadi masalah di sini.
Sedangkan untuk memiliki semua properti sebagai publik ... Mungkin pikirkan seperti ini, jika Anda ingin menyinkronkan akses ke semua properti ini di antara utas Anda akan mengalami kesulitan karena Anda mungkin harus menulis kode sinkronisasi di mana pun mereka berada bekas. Namun, jika semua properti tertutup oleh getter / setter maka Anda dapat dengan mudah membangun sinkronisasi ke dalam kelas.
Saya pikir ketika Anda mengatakan "sulit untuk menguji perilaku" argumen itu berbicara sendiri. Tes Anda mungkin harus memeriksa apakah bukan nol atau sesuatu seperti itu (setiap tempat digunakan properti). Saya tidak benar-benar tahu karena saya tidak tahu seperti apa tes / aplikasi Anda.
Anda benar terlalu banyak properti, dapatkah Anda mencoba menggunakan pewarisan (jika propertinya cukup mirip) dan kemudian memiliki daftar / array menggunakan obat generik? Kemudian Anda bisa menulis getter / setter untuk mengakses informasi dan menyederhanakan cara Anda akan berinteraksi semua properti.
sumber
Seperti yang Anda katakan,
Foo
adalah objek bisnis. Itu harus merangkum beberapa perilaku dalam metode sesuai dengan domain Anda dan datanya harus tetap dienkapsulasi mungkin.Pada contoh yang Anda tunjukkan,
Foo
lebih mirip DTO dan bertentangan dengan semua prinsip OOP (lihat Model Domain Anemik )!Sebagai perbaikan saya sarankan:
Ini bisa menjadi kelas refactored potensial dengan komentar ini:
sumber
"Kita sekarang dapat menguji unit metode refactored itu. Karena, untuk alasan x, y, z klien tidak dapat diuji.
Sejauh argumen di atas dapat dibuat, secara agregat:
sumber