Bir dosyadan ayarları nereye yükleyebilir ve depolayabilirim?


9

Bu sorunun ayarları bir dosyadan yükleyen çoğu program için geçerli olduğunu düşünüyorum. Benim sorum bir programlama bakış açısından, ve gerçekten farklı sınıflar ve erişilebilirlik açısından bir dosyadan ayarların yüklenmesi ile nasıl başa çıkılacağı. Örneğin:

  • Bir program basit bir settings.inidosyaya sahipse , içeriği load()bir sınıf yöntemine veya belki de kurucuya yüklenmelidir ?
  • Değerler public staticdeğişkenlerde saklanmalı mı yoksa staticözellik alma ve ayarlama yöntemleri olmalı mı?
  • Dosyanın mevcut olmaması veya okunamaması durumunda ne olmalı? Programın geri kalanının bu özellikleri alamayacağını nasıl bilebilirsiniz?
  • vb.

Bunu doğru yerde sormamı umuyorum. Soruyu olabildiğince dilbilimsiz bir şekilde sormak istedim, ancak özellikle miras gibi şeylere sahip dillere - özellikle Java ve C # .NET'e odaklanıyorum.


1
.NET için, ayarları almak için App.config ve System.Configuration.ConfigurationManager sınıfını kullanmak en iyisidir
Gibson

@Gibson Sadece .NET'te başlıyorum, bu yüzden beni bu sınıf için iyi bir öğreticiye bağlayabilir misiniz?
Andy

Stackoverflow üzerinde çok sayıda cevap var. Çabuk arama şunları ortaya koyuyor: stackoverflow.com/questions/114527/… ve stackoverflow.com/questions/13043530/… MSDN'de buradan başlayarak da çok fazla bilgi olacak msdn.microsoft.com/tr-tr/library/ms228063(v = vs.100) .aspx ve msdn.microsoft.com/en-us/library/ms184658(v=vs.110).aspx
Gibson

@Gibson Bu bağlantılar için teşekkür ederim. Çok faydalı olacaklar.
Andy

Yanıtlar:


8

Bu gerçekten çok önemli bir soru ve hemen hemen her uygulamanın temel bir parçası olmasına rağmen yeterince önem verilmediği için yanlış yapılıyor. İşte yönergelerim:

Tüm ayarları içeren yapılandırma sınıfınız yalnızca düz bir eski veri türü, struct / class olmalıdır:

class Config {
    int prop1;
    float prop2;
    SubConfig subConfig;
}

Yöntemlere sahip olmamalı ve miras içermemelidir (bir değişken alanı uygulamak için dilinizde sahip olduğunuz tek seçenek olmadığı sürece - bir sonraki paragrafa bakın). Ayarları daha küçük özel yapılandırma sınıflarına gruplamak için kompozisyon kullanabilir ve kullanmalıdır (örn. Yukarıdaki subConfig). Bu şekilde yaparsanız, birim testlerde ve genel olarak uygulamada en az bağımlılığa sahip olacağı için etrafta dolaşmak ideal olacaktır.

Farklı kurulumlar için yapıların heterojen olması durumunda, büyük olasılıkla değişken türleri kullanmanız gerekecektir. Değeri sağ (alt) yapılandırma sınıfına yayınlamak için değeri okuduğunuzda bir noktada dinamik bir döküm eklemeniz gerektiği kabul edilir ve şüphesiz bu başka bir yapılandırma ayarına bağlı olacaktır.

Bunu yaparak tüm ayarları alanlar olarak yazmak konusunda tembel olmamalısınız:

class Config {
    Dictionary<string, string> values;
};

Bu, hangi alanlarla uğraştığını bilmek zorunda olmayan genelleştirilmiş bir serileştirme sınıfı yazabileceğiniz anlamına geldiği için caziptir, ancak yanlıştır ve nedenini bir anda açıklayacağım.

Konfigürasyonun seri hale getirilmesi tamamen ayrı bir sınıfta yapılır. Bunu yapmak için hangi API'yi veya kütüphaneyi kullanırsanız kullanın, serileştirme işlevinizin gövdesi temel olarak dosyadaki yol / anahtardan nesnenin üzerindeki alana eşleme anlamına gelen girişler içermelidir. Bazı diller iyi bir içgözlem sağlar ve bunu sizin için kutudan çıkarır, diğerleri ise eşlemeyi açıkça yazmanız gerekir, ancak asıl önemli olan eşleştirmeyi yalnızca bir kez yazmanız gerekir. Örneğin c ++ boost program seçenekleri ayrıştırıcı belgelerinden uyarlanmış bu özü düşünün:

struct Config {
   int opt;
} conf;
po::options_description desc("Allowed options");
desc.add_options()
    ("optimization", po::value<int>(&conf.opt)->default_value(10);

Son satırın temelde "optimizasyon" ifadesinin Config :: opt ile eşleştiğini ve ayrıca beklediğiniz türde bir bildirim olduğunu unutmayın. Tür beklediğiniz gibi değilse, dosyadaki parametre gerçekten bir kayan nokta veya int değilse veya yoksa yapılandırma okumasının başarısız olmasını istersiniz. Ie Dosyayı okuduğunuzda hata oluşmalıdır, çünkü sorun dosyanın formatı / doğrulamasıyla ilgilidir ve bir dış / dönüş kodu atmanız ve tam sorunu bildirmeniz gerekir. Bunu programda daha sonraya ertelememelisiniz. Bu nedenle, dosya okunurken başarısız olmayacak - değer gerekli olana kadar döküm ertelendiğinden, yukarıda belirtildiği gibi tüm Sözlük stili Conf'yi yakalamaya cazip olmamalısınız.

Config sınıfını bazı şekillerde salt okunur yapmalısınız - sınıfın içeriğini oluştururken ve dosyadan başlatırken bir kez ayarlamalısınız. Uygulamanızda değişen sabit ayarların yanı sıra değişen dinamik ayarlara ihtiyacınız varsa, yapılandırma sınıfınızın bitlerinin salt okunur olmamasına izin vermek yerine dinamik olanları işlemek için ayrı bir sınıfınız olmalıdır. .

İdeal olarak dosyayı programınızda tek bir yerde okursunuz, yani sadece bir " ConfigReader" örneğiniz vardır . Ancak, Config örneğini ihtiyacınız olan yere iletmekle uğraşıyorsanız, ikinci bir ConfigReader'a sahip olmak global bir yapılandırma sunmaktan daha iyidir (ki tahmin ediyorum ki OP'nin "statik" "), bu da beni bir sonraki noktama getiriyor:

Singleton'un baştan çıkarıcı siren şarkısından kaçının: "Sizi o sınıf sınıfından geçirmek zorunda kalacağım, tüm inşaatçılarınız güzel ve temiz olacak. Devam edin, çok kolay olacak." Gerçek, iyi tasarlanmış test edilebilir bir mimariyle, Config sınıfını veya bir kısmını uygulamanızın birçok sınıfından geçirmeniz gerekmeyecektir. Üst düzey sınıfınızda, ana () işlevinizde veya her neyse bulacağınız şey, conf'i bileşen sınıflarınıza daha sonra bir araya getirdiğiniz argümanlar olarak sağlayacağınız bireysel değerlere çözersiniz (manuel bağımlılık) enjeksiyon). Tek bir / global / statik conf, birim testini uygulamanızı uygulamak ve anlamak için daha zor hale getirecektir - örneğin, yeni geliştiricileri ekibinize, şeyleri test etmek için küresel durumu ayarlamaları gerektiğini bilmeyecek şekilde karıştırır.

Diliniz özellikleri destekliyorsa, bu amaç için kullanmalısınız. Bunun nedeni, bir veya daha fazla ayara bağlı 'türetilmiş' yapılandırma ayarlarının eklenmesinin çok kolay olacağı anlamına gelir. Örneğin

int Prop1 { get; }
int Prop2 { get; }
int Prop3 { get { return Prop1*Prop2; }

Diliniz özellik deyimini yerel olarak desteklemiyorsa, aynı efekti elde etmek için bir geçici çözüm olabilir veya sadece bonus ayarlarını sağlayan bir sarıcı sınıfı oluşturursunuz. Aksi takdirde mülklerin faydasını sağlayamazsanız, manuel olarak yazmak ve sadece bazı OO-tanrıyı memnun etmek için alıcıları / ayarlayıcıları kullanmak zaman kaybıdır. Sade eski bir tarlada daha iyi olacaksın.

Öncelik sırasına göre farklı yerlerden birden fazla yapılandırmayı birleştirmek ve almak için bir sisteme ihtiyacınız olabilir. Bu öncelik sırası iyi tanımlanmalı ve tüm geliştiriciler / kullanıcılar tarafından anlaşılmalıdır, örn. HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE. Yapılandırmalarınızı salt okunur olarak saklayabilmeniz için bu işlevsel stili yapmalısınız:

final_conf = merge(user_conf, machine_conf)

ziyade:

conf.update(user_conf)

Son olarak, seçtiğiniz çerçeve / dil kendi yerleşik, iyi bilinen yapılandırma mekanizmalarını sağlıyorsa, bunu kendi başınıza döndürmek yerine kullanmanın faydalarını göz önünde bulundurmanız gerektiğine eklemeliyim.

Yani. Dikkate alınacak birçok özellik - doğru olsun ve uygulama mimarinizi derinden etkileyecek, hataları azaltacak, işleri kolayca test edilebilir hale getirecek ve sizi başka bir yerde iyi tasarım kullanmaya zorlayacak.


+1 Cevabınız için teşekkür ederim. Daha önce kullanıcı ayarlarını sakladığımda .ini, insan tarafından okunabilir olması için bir dosyadan yeni okudum , ancak değişkenleri içeren bir sınıfı serileştirmemi mi öneriyorsunuz?
Andy

.ini'nin ayrıştırılması kolaydır ve .ini'yi olası bir biçim olarak içeren birçok API da vardır. Metin ayrıştırma homurdanıyor iş yapmak için bu tür API'leri kullanmanızı öneririz ama sonuç POD sınıfı başlatmış olması gerekir. Serileşme terimini bir sınıfın bazı biçimlerdeki alanlarına kopyalama veya okuma genel anlamda kullanıyorum, bir sınıfı doğrudan ikili dosyaya / diziden serileştirmenin daha spesifik bir tanımını değil (tipik olarak java.io için anlaşıldığı gibi). ).
Benedict

Şimdi biraz daha iyi anlıyorum. Yani aslında tüm alanları / ayarları içeren bir sınıfa sahip olduğunuzu ve o sınıftaki değerleri dosyadan ayarlayacağınızı mı söylüyorsunuz? Daha sonra, diğer sınıflardaki birinci sınıftan değerleri nasıl almalıyım?
Andy

Durum bazında. Üst düzey sınıflarınızdan bazıları, çok fazla parametreye güveniyorlarsa, kendilerine iletilen tüm Config örneğine başvurmak zorunda kalabilirler. Diğer sınıflar sadece bir veya iki parametreye ihtiyaç duyar, ancak bunları Config nesnesiyle eşleştirmeye gerek yoktur (test edilmelerini daha da kolaylaştırır), birkaç parametreyi uygulamanızdan geçirin. Cevabımda belirtildiği gibi, test edilebilir / DI odaklı bir mimari ile inşa ederseniz, ihtiyacınız olan değerleri elde etmek genellikle zor olmayacaktır. Global erişimi tehlikede kullanın.
Benedict

Peki, kalıtımın dahil edilmesi gerekmiyorsa config nesnesinin sınıflara aktarılmasını nasıl önerirsiniz? Bir kurucu aracılığıyla mı? Böylece, programın ana yönteminde, Config sınıfında değerleri depolayan okuyucu sınıfı başlatılır, daha sonra ana yöntem Config nesnesini veya bağımsız değişkenleri diğer sınıflara geçirir?
Andy

4

Genel olarak (bence), uygulamanın yapılandırmanın nasıl saklandığını ele almasına ve yapılandırmayı modüllerinize geçirmesine izin vermek en iyisidir. Bu esneklik verir nasıl dosyaları veya webservices veya veritabanları veya hedef böylece ayarlar kaydedilir ...

Ayrıca, bu başarısızlığın ne anlama geldiğini en iyi bilen, "işler başarısız olduğunda ne olur" yükünü yükler.

Ayrıca , dosya sistemine dokunmak veya statik erişimin getirdiği eşzamanlılık sorunlarıyla uğraşmak yerine bir yapılandırma nesnesine geçebildiğinizde birim test etmeyi tonlarca kolaylaştırır.


Cevabınız için teşekkür ederim ama tam olarak anlamıyorum. Uygulamayı yazarken konuşuyorum, bu yüzden yapılandırma ile ilgilenecek bir şey yok ve bu nedenle bir programcı olarak bir hatanın ne anlama geldiğini biliyorum.
Andy

@andy - emin, ancak modüller yapılandırmaya doğrudan erişirse, hangi bağlamda çalıştıklarını bilmezler, bu nedenle yapılandırma sorunlarının nasıl ele alınacağını belirleyemezler. Uygulama yüklemeyi yapıyorsa, içeriği bildiği için herhangi bir sorunla başa çıkabilir. Bazı uygulamalar varsayılan bir değere geçmek isteyebilir, diğerleri iptal etmek isteyebilir, vb.
Telastyn

Sadece burada açıklığa kavuşturmak için, modüller tarafından sınıflara mı yoksa bir programın gerçek uzantılarına mı atıfta bulunuyorsunuz? Çünkü ayarları en iyi nerede ana programa kod içinde depolamak ve diğer sınıfların ayarlara erişmesine izin vermek için soruyorum. Bu yüzden bir yapılandırma dosyası yüklemek ve daha sonra ayarı bir yerde / bir şekilde saklamak istiyorum, bu yüzden dosyaya başvurmaya devam etmek zorunda değilim.
Andy

0

Sınıfları programlamak için .NET kullanıyorsanız, Kaynaklar, web.config ve hatta özel bir dosya gibi farklı seçenekleriniz vardır.

Kaynaklar veya web.config kullanıyorsanız, veriler aslında XML dosyasında saklanır, ancak yükleme daha hızlıdır.

Bu dosyalardan veri almak ve başka bir yerde saklamak, varsayılan olarak belleğe yüklendiği için belleğin iki kez kullanılması gibi olacaktır.

Diğer herhangi bir dosya veya programlama dili için, Benedict'in yukarıdaki cevabı işe yarayacaktır.


Cevabınız için teşekkürler ve yavaş cevap için üzgünüm. Kaynakları kullanmak iyi bir seçenek olurdu, ama ben sadece aynı programı diğer dillerde geliştirmeyi planlıyorum gibi görüyorum muhtemelen benim için en iyi seçenek olmadığını fark ettim, bu yüzden bir XML veya JSON dosyasına sahip olmayı tercih ederim ama aynı dosyanın diğer programlama dillerinde okunabilmesi için kendi sınıfımda oku.
Andy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.