Saya cukup pragmatis, tetapi perhatian utama saya di sini adalah bahwa Anda mungkin membiarkan ini ConfigBlock
mendominasi desain antarmuka Anda dengan cara yang mungkin buruk. Ketika Anda memiliki sesuatu seperti ini:
explicit MyGreatClass(const ConfigBlock& config);
... antarmuka yang lebih tepat mungkin seperti ini:
MyGreatClass(int foo, float bar, const string& baz);
... Yang bertentangan dengan hanya memetik ceri foo/bar/baz
bidang ini dari besar-besaran ConfigBlock
.
Desain Antarmuka Malas
Di sisi positifnya, jenis desain ini memudahkan untuk merancang antarmuka yang stabil untuk konstruktor Anda, misalnya, karena jika Anda akhirnya membutuhkan sesuatu yang baru, Anda bisa memuatnya ke dalam ConfigBlock
(mungkin tanpa perubahan kode) dan kemudian menambahkan pilih barang baru apa pun yang Anda butuhkan tanpa perubahan antarmuka apa pun, hanya perubahan pada implementasi MyGreatClass
.
Jadi ini baik pro dan kontra bahwa ini membebaskan Anda merancang antarmuka yang lebih hati-hati yang hanya menerima input yang sebenarnya dibutuhkan. Ini menerapkan pola pikir, "Beri saya gumpalan data yang besar ini, saya akan memilih apa yang saya butuhkan darinya" sebagai kebalikan dari sesuatu yang lebih seperti, "Parameter yang tepat inilah yang diperlukan antarmuka ini untuk bekerja."
Jadi pasti ada beberapa pro di sini, tetapi mereka mungkin sangat kalah dengan kontra.
Kopel
Dalam skenario ini, semua kelas yang dibangun dari ConfigBlock
instance akhirnya memiliki dependensi mereka terlihat seperti ini:
Ini bisa menjadi PITA, misalnya, jika Anda ingin menguji unit Class2
dalam diagram ini secara terpisah. Anda mungkin harus mensimulasikan secara dangkal berbagai ConfigBlock
input yang berisi bidang yang relevan Class2
tertarik untuk dapat mengujinya di bawah berbagai kondisi.
Dalam segala jenis konteks baru (baik tes unit atau proyek baru), setiap kelas seperti itu dapat menjadi lebih berat untuk digunakan kembali, karena pada akhirnya kita harus selalu membawa ConfigBlock
serta untuk perjalanan, dan mengaturnya demikian.
Dapat digunakan kembali / Deployability / Testability
Alih-alih jika Anda merancang antarmuka ini dengan tepat, kami dapat memisahkannya dari ConfigBlock
dan berakhir dengan sesuatu seperti ini:
Jika Anda perhatikan dalam diagram di atas, semua kelas menjadi independen (kopling aferen / keluarnya berkurang 1).
Hal ini menyebabkan kelas yang lebih mandiri (setidaknya tidak tergantung dari ConfigBlock
) yang dapat lebih mudah untuk (kembali) digunakan / diuji dalam skenario / proyek baru.
Sekarang Client
kode ini akhirnya menjadi salah satu yang harus bergantung pada segalanya dan mengumpulkan semuanya bersama-sama. Beban akhirnya ditransfer ke kode klien ini untuk membaca bidang yang sesuai dari a ConfigBlock
dan meneruskannya ke kelas yang sesuai sebagai parameter. Namun kode klien seperti itu pada umumnya dirancang secara sempit untuk konteks tertentu, dan potensinya untuk digunakan kembali biasanya akan menjadi nihil atau ditutup (mungkin itu adalah main
fungsi titik masuk aplikasi Anda atau semacamnya).
Jadi dari sudut pandang usabilitas dan pengujian, ini dapat membantu untuk membuat kelas-kelas ini lebih mandiri. Dari sudut pandang antarmuka untuk mereka yang menggunakan kelas Anda, itu juga dapat membantu untuk secara eksplisit menyatakan parameter apa yang mereka butuhkan alih-alih hanya satu besar ConfigBlock
yang memodelkan seluruh semesta bidang data yang diperlukan untuk semuanya.
Kesimpulan
Secara umum, jenis desain berorientasi kelas yang tergantung pada monolit yang memiliki semua yang dibutuhkan cenderung memiliki karakteristik seperti ini. Penerapannya, penerapannya, dapat digunakan kembali, dapat diuji, dll. Sebagai hasilnya dapat terdegradasi secara signifikan. Namun mereka dapat menyederhanakan desain antarmuka jika kita mencoba putaran positif di atasnya. Terserah Anda untuk mengukur pro dan kontra dan memutuskan apakah trade-off itu sepadan. Biasanya jauh lebih aman untuk melakukan kesalahan terhadap desain semacam ini di mana Anda memilih ceri dari monolit di kelas yang umumnya dimaksudkan untuk memodelkan desain yang lebih umum dan dapat diterapkan secara luas.
Terakhir tapi bukan yang akhir:
extern CodingBlock MyCodingBlock;
... ini berpotensi lebih buruk (lebih condong?) dalam hal karakteristik yang dijelaskan di atas daripada pendekatan injeksi dependensi, karena akhirnya menggabungkan kelas Anda tidak hanya untuk ConfigBlocks
, tetapi langsung ke contoh spesifik itu. Itu lebih lanjut menurunkan penerapan / penyebaran / testability.
Saran umum saya adalah kesalahan dalam mendesain antarmuka yang tidak bergantung pada monolit semacam ini untuk memberikan parameternya, setidaknya untuk kelas yang paling umum berlaku yang Anda desain. Dan hindari pendekatan global tanpa injeksi ketergantungan jika Anda bisa kecuali Anda benar-benar memiliki alasan yang sangat kuat dan percaya diri untuk tidak menghindarinya.
switch
pernyataan atauif
pengujian pernyataan terhadap nilai yang dibaca dari file konfigurasi.Iya. Lebih baik memusatkan konstanta dan nilai runtime dan kode untuk membacanya.
Ini buruk: sebagian besar konstruktor Anda tidak akan membutuhkan sebagian besar nilai. Sebagai gantinya, buat antarmuka untuk segala hal yang tidak sepele untuk dikonstruksi:
kode lama (proposal Anda):
kode baru:
instantiate MyGreatClass:
Di sini,
current_config_block
adalah instance dariConfigBlock
kelas Anda (yang berisi semua nilai Anda) danMyGreatClass
kelas menerimaGreatClassData
instance. Dengan kata lain, hanya berikan kepada konstruktor data yang mereka butuhkan, dan tambahkan fasilitas kepada AndaConfigBlock
untuk membuat data itu.Kode ini menunjukkan bahwa Anda akan memiliki instance CodingBlock global. Jangan lakukan itu: biasanya Anda harus memiliki instance yang dideklarasikan secara global, di titik masuk apa pun yang digunakan aplikasi Anda (fungsi utama, DllMain, dll) dan meneruskannya sebagai argumen di mana pun Anda butuhkan (tetapi seperti yang dijelaskan di atas, Anda tidak boleh lulus seluruh kelas di sekitar, cukup tampilkan antarmuka di sekitar data, dan lewati itu).
Juga, jangan ikat kelas klien Anda (Anda
MyGreatClass
) dengan jenisCodingBlock
; Ini berarti bahwa, jika AndaMyGreatClass
mengambil string dan lima bilangan bulat, Anda akan lebih baik melewati string dan bilangan bulat itu, daripada Anda akan meneruskan dalam aCodingBlock
.sumber
Jawaban singkat:
Anda tidak memerlukan semua pengaturan untuk setiap modul / kelas dalam kode Anda. Jika Anda melakukannya, maka ada sesuatu yang salah dengan desain berorientasi objek Anda. Terutama dalam kasus pengujian unit pengaturan semua variabel yang tidak Anda butuhkan dan melewati objek itu tidak akan membantu dengan membaca atau memelihara.
sumber
ConfigBlock
kelas Anda . Intinya di sini adalah untuk tidak menyediakan semua, dalam hal ini, konteks keadaan sistem, hanya khusus, nilai yang diperlukan untuk melakukannya.