Di mana memuat dan menyimpan pengaturan dari file?

9

Saya pikir pertanyaan ini harus berlaku untuk sebagian besar program yang memuat pengaturan dari file. Pertanyaan saya adalah dari sudut pandang pemrograman, dan itu benar-benar bagaimana menangani pemuatan pengaturan dari file dalam hal kelas yang berbeda dan aksesibilitas. Contohnya:

  • Jika suatu program memiliki settings.inifile sederhana , haruskah isinya dimuat dalam load()metode kelas, atau mungkin konstruktor?
  • Haruskah nilai disimpan dalam public staticvariabel, atau haruskah ada staticmetode untuk mendapatkan dan mengatur properti?
  • Apa yang harus terjadi jika file tidak ada atau tidak dapat dibaca? Bagaimana Anda akan membiarkan sisa program tahu bahwa tidak bisa mendapatkan properti itu?
  • dll.

Saya berharap saya menanyakan ini di tempat yang tepat di sini. Saya ingin membuat pertanyaan sebagai bahasa agnostik mungkin, tapi saya terutama berfokus pada bahasa yang memiliki hal-hal seperti warisan - terutama Java dan C # .NET.

Andy
sumber
1
Untuk .NET, yang terbaik adalah menggunakan App.config dan kelas System.Configuration.ConfigurationManager untuk mendapatkan pengaturan
Gibson
@ Gibson Saya baru memulai. NET, jadi bisakah Anda menautkan saya ke tutorial yang bagus untuk kelas itu?
Andy
Stackoverflow memiliki banyak jawaban. Pencarian cepat mengungkapkan: stackoverflow.com/questions/114527/… dan stackoverflow.com/questions/13043530/... Juga akan ada banyak info tentang MSDN, mulai dari sini msdn.microsoft.com/en-us/library/ms228063(v = vs.100) .aspx dan msdn.microsoft.com/en-us/library/ms184658(v=vs.110).aspx
Gibson
@ Gibson Terima kasih atas tautannya. Mereka akan sangat berguna.
Andy

Jawaban:

8

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.

Benediktus
sumber
+1 Terima kasih atas jawabannya. Sebelumnya ketika saya telah menyimpan pengaturan pengguna saya baru saja membaca dari file seperti .inisehingga itu dapat dibaca manusia, tetapi apakah Anda menyarankan saya harus membuat serial kelas dengan variabel dalam?
Andy
.ini mudah diurai, dan ada juga banyak API yang menyertakan .ini sebagai format yang memungkinkan. Saya menyarankan agar Anda menggunakan API tersebut untuk membantu Anda melakukan pekerjaan kasar dari penguraian tekstual tetapi bahwa hasil akhirnya adalah bahwa Anda telah menginisialisasi kelas POD. Saya menggunakan istilah cerita bersambung dalam arti umum menyalin atau membaca di bidang kelas dari beberapa format, bukan definisi yang lebih spesifik dari serialisasi kelas langsung ke / dari biner (seperti yang biasanya dipahami untuk mengatakan java.io.Serializable ).
Benediktus
Sekarang saya mengerti sedikit lebih baik. Jadi pada dasarnya Anda mengatakan memiliki satu kelas dengan semua bidang / pengaturan di, dan memiliki yang lain untuk mengatur nilai di kelas itu dari file? Lalu, bagaimana saya harus mendapatkan nilai dari kelas pertama di kelas lain?
Andy
Kasus per kasus. Beberapa kelas tingkat atas Anda mungkin hanya perlu referensi ke seluruh instance Config yang diteruskan ke mereka jika mereka bergantung pada begitu banyak parameter. Kelas-kelas lain mungkin hanya memerlukan satu atau dua parameter, tetapi tidak perlu memasangkannya ke objek Config (membuatnya lebih mudah untuk diuji), cukup berikan beberapa parameter ke aplikasi Anda. Seperti yang disebutkan dalam jawaban saya, jika Anda membangun dengan arsitektur berorientasi testable / DI mendapatkan nilai-nilai di mana Anda membutuhkannya umumnya tidak akan sulit. Gunakan akses global atas risiko Anda.
Benediktus
Jadi, bagaimana Anda menyarankan bahwa objek config dilewatkan ke kelas jika warisan tidak perlu dilibatkan? Melalui konstruktor? Jadi, dalam metode utama program, kelas pembaca dimulai yang menyimpan nilai dalam kelas Config, lalu metode utama melewati objek Config, atau variabel individual ke kelas lain?
Andy
4

Secara umum (menurut saya), yang terbaik adalah membiarkan aplikasi menangani bagaimana konfigurasi disimpan, dan meneruskan konfigurasi ke modul Anda. Ini memungkinkan fleksibilitas dalam cara pengaturan disimpan sehingga Anda dapat menargetkan file atau layanan web atau ...

Plus, itu menempatkan beban "apa yang terjadi ketika sesuatu gagal" pada aplikasi, siapa yang paling tahu apa arti kegagalan itu.

Dan itu membuatnya lebih mudah untuk unit test ketika Anda hanya bisa meneruskan objek konfigurasi daripada harus menyentuh sistem file atau menangani masalah konkurensi yang diperkenalkan oleh akses statis.

Telastyn
sumber
Terima kasih atas jawaban Anda, tetapi saya tidak terlalu memahaminya. Saya berbicara tentang ketika menulis aplikasi, jadi tidak ada yang berurusan dengan konfigurasi, dan oleh karena itu, sebagai seorang programmer, saya tahu apa artinya kegagalan.
Andy
@ bagus - tentu, tetapi jika modul mengakses konfigurasi secara langsung, mereka tidak tahu konteks apa yang mereka kerjakan, sehingga mereka tidak dapat menentukan bagaimana menangani masalah konfigurasi. Jika aplikasi melakukan pemuatan, ia dapat menangani masalah apa pun, karena ia mengetahui konteksnya. Beberapa aplikasi mungkin ingin gagal ke nilai default, yang lain ingin dibatalkan, dll.
Telastyn
Untuk memperjelas di sini, dengan modul, apakah Anda merujuk ke kelas, atau ekstensi aktual ke suatu program? Karena saya bertanya tentang tempat terbaik untuk menyimpan pengaturan di dalam kode ke program utama dan bagaimana cara memungkinkan kelas lain mengakses pengaturan. Jadi saya ingin memuat file konfigurasi dan kemudian menyimpan pengaturan di suatu tempat / entah bagaimana jadi saya tidak harus terus merujuk ke file.
Andy
0

Jika Anda menggunakan .NET untuk memprogram kelas, Anda memiliki opsi berbeda seperti Sumber Daya, web.config, atau bahkan file kustom.

Jika Anda menggunakan Sumber Daya atau web.config, data sebenarnya disimpan dalam file XML, tetapi, pemuatannya lebih cepat.

Mengambil data dari file-file ini dan menyimpannya di lokasi lain akan seperti penggunaan memori ganda karena ini dimuat ke memori secara default.

Untuk file atau bahasa pemrograman lain, jawaban di atas oleh Benedict akan berfungsi.

Kiran
sumber
Terima kasih atas jawaban Anda dan maaf atas jawaban yang lambat. Menggunakan Sumber Daya akan menjadi pilihan yang baik, tetapi saya baru menyadari bahwa itu mungkin bukan pilihan terbaik bagi saya melihat seolah-olah saya berencana untuk mengembangkan program yang sama dalam bahasa lain, jadi saya lebih suka memiliki file XML atau JSON tetapi membacanya di kelas saya sendiri sehingga file yang sama dapat dibaca dalam bahasa pemrograman lain.
Andy