Ini sebenarnya adalah pertanyaan yang sangat penting dan sering dilakukan secara salah karena tidak diberikan kepentingan yang cukup meskipun itu adalah bagian inti dari hampir setiap aplikasi. Inilah pedoman saya:
Kelas config Anda, yang berisi semua pengaturan harus hanya tipe data lama, struct / class:
class Config {
int prop1;
float prop2;
SubConfig subConfig;
}
Seharusnya tidak perlu memiliki metode dan tidak boleh melibatkan pewarisan (kecuali itu adalah satu-satunya pilihan yang Anda miliki dalam bahasa Anda untuk menerapkan bidang varian - lihat paragraf berikutnya). Itu dapat dan harus menggunakan komposisi untuk mengelompokkan pengaturan ke dalam kelas konfigurasi spesifik yang lebih kecil (mis. SubConfig di atas). Jika Anda melakukannya dengan cara ini akan ideal untuk lulus dalam tes unit dan aplikasi secara umum karena akan memiliki ketergantungan minimal.
Anda mungkin perlu menggunakan jenis varian, jika konfigurasi untuk pengaturan berbeda heterogen dalam struktur. Diterima bahwa Anda harus memasukkan cast dinamis di beberapa titik ketika Anda membaca nilai untuk melemparkannya ke kelas konfigurasi (sub-) yang tepat, dan tidak diragukan lagi ini akan tergantung pada pengaturan konfigurasi lain.
Anda tidak boleh malas mengetik semua pengaturan sebagai bidang dengan hanya melakukan ini:
class Config {
Dictionary<string, string> values;
};
Ini menggoda karena itu berarti Anda dapat menulis kelas serialisasi umum yang tidak perlu tahu dengan bidang apa yang dihadapinya, tetapi itu salah dan saya akan menjelaskan mengapa sebentar lagi.
Serialisasi dari konfigurasi dilakukan dalam kelas yang benar-benar terpisah. Apa pun API atau pustaka yang Anda gunakan untuk melakukan ini, isi fungsi serialisasi Anda harus berisi entri yang pada dasarnya sama dengan menjadi peta dari path / kunci dalam file ke bidang pada objek. Beberapa bahasa memberikan introspeksi yang baik dan dapat melakukan ini untuk Anda di luar kebiasaan, yang lain Anda harus menulis pemetaan secara eksplisit, tetapi kuncinya adalah Anda hanya perlu menulis pemetaan sekali saja. Misalnya, pertimbangkan ekstrak ini yang saya adaptasi dari dokumentasi parser opsi program c ++ boost:
struct Config {
int opt;
} conf;
po::options_description desc("Allowed options");
desc.add_options()
("optimization", po::value<int>(&conf.opt)->default_value(10);
Perhatikan bahwa baris terakhir pada dasarnya mengatakan "optimasi" memetakan ke Config :: opt dan juga bahwa ada deklarasi tipe yang Anda harapkan. Anda ingin pembacaan konfigurasi gagal jika jenisnya tidak seperti yang Anda harapkan, jika parameter dalam file tersebut bukan float atau int, atau tidak ada. Yaitu Kegagalan harus terjadi ketika Anda membaca file karena masalahnya adalah dengan format / validasi file dan Anda harus membuang kode kecuali / kembali dan melaporkan masalah yang sebenarnya. Anda seharusnya tidak menunda ini nanti di program. Itu sebabnya Anda tidak perlu tergoda untuk menangkap semua gaya Kamus Conf seperti disebutkan di atas yang tidak akan gagal saat file dibaca - karena casting ditunda hingga nilainya diperlukan.
Anda harus membuat kelas Config hanya-baca dengan cara tertentu - mengatur konten kelas satu kali saat Anda membuatnya dan menginisialisasinya dari file. Jika Anda perlu memiliki pengaturan dinamis di aplikasi Anda yang berubah, dan juga yang tidak, Anda harus memiliki kelas terpisah untuk menangani yang dinamis daripada mencoba untuk membiarkan bit kelas konfigurasi Anda tidak hanya-baca .
Idealnya Anda baca di file dalam satu tempat dalam program Anda yaitu Anda hanya memiliki satu contoh dari " ConfigReader
". Namun, jika Anda kesulitan untuk mendapatkan instance Config yang diedarkan ke tempat Anda membutuhkannya, lebih baik memiliki ConfigReader kedua daripada memperkenalkan konfigurasi global (yang saya duga adalah apa yang OP maksud dengan "statis". "), yang membawa saya ke poin saya berikutnya:
Hindari lagu sirene yang menggoda dari si singleton: "Aku akan menyelamatkanmu karena harus lulus kelas sekelas itu, semua konstruktormu akan indah dan bersih. Ayo, itu akan sangat mudah." Kebenaran dengan arsitektur teruji yang dirancang dengan baik Anda tidak perlu melewati kelas Config, atau bagian dari itu melalui banyak kelas aplikasi Anda. Apa yang akan Anda temukan, di kelas tingkat atas, fungsi utama Anda () atau apa pun, Anda akan mengurai conf menjadi nilai-nilai individual, yang akan Anda berikan kepada kelas komponen Anda sebagai argumen yang kemudian Anda kembalikan bersama-sama (ketergantungan manual injeksi). Konfigurasi tunggal / global / statis akan membuat unit menguji aplikasi Anda lebih sulit untuk diimplementasikan dan dipahami - mis. Itu akan membingungkan pengembang baru untuk tim Anda yang tidak akan tahu mereka harus mengatur keadaan global untuk menguji barang-barang.
Jika bahasa Anda mendukung properti Anda harus menggunakannya untuk tujuan ini. Alasannya adalah itu berarti akan sangat mudah untuk menambahkan pengaturan konfigurasi 'turunan' yang bergantung pada satu atau lebih pengaturan lainnya. misalnya
int Prop1 { get; }
int Prop2 { get; }
int Prop3 { get { return Prop1*Prop2; }
Jika bahasa Anda tidak mendukung idiom properti, bahasa tersebut mungkin memiliki solusi untuk mencapai efek yang sama, atau Anda cukup membuat kelas pembungkus yang menyediakan pengaturan bonus. Jika Anda tidak bisa memberikan manfaat properti, jika tidak, buang waktu untuk menulis secara manual dan menggunakan getter / setter hanya untuk tujuan menyenangkan beberapa dewa-OO. Anda akan lebih baik dengan bidang tua yang sederhana.
Anda mungkin memerlukan sistem untuk menggabungkan dan mengambil banyak konfigurasi dari berbagai tempat sesuai urutan prioritas. Urutan yang diutamakan itu harus didefinisikan dengan baik dan dipahami oleh semua pengembang / pengguna misalnya mempertimbangkan windows registry HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE. Anda harus melakukan gaya fungsional ini sehingga Anda dapat membuat konfigurasi Anda hanya baca yaitu:
final_conf = merge(user_conf, machine_conf)
daripada:
conf.update(user_conf)
Saya akhirnya harus menambahkan itu tentu saja jika kerangka kerja / bahasa yang Anda pilih menyediakan built-in, mekanisme konfigurasi terkenal Anda harus mempertimbangkan manfaat menggunakan itu daripada menggulir sendiri.
Begitu. Banyak aspek yang perlu dipertimbangkan - lakukan dengan benar dan itu akan sangat mempengaruhi arsitektur aplikasi Anda, mengurangi bug, membuat hal-hal mudah diuji dan semacam memaksa Anda untuk menggunakan desain yang baik di tempat lain.