Uygulama ayarlarını yüklemenin en iyi yolu


24

Bir Java uygulamasının ayarlarını korumanın basit bir yolu, belirli bir değerle ilişkilendirilmiş her bir ayarın tanımlayıcısını içeren ".properties" uzantılı bir metin dosyası ile gösterilir (bu değer bir sayı, dize, tarih vb. Olabilir). . C # benzer bir yaklaşım kullanır, ancak metin dosyası "App.config" olarak adlandırılmalıdır. Her iki durumda da, kaynak kodunda ayarları okumak için belirli bir sınıfı başlatmalısınız: bu sınıf, belirtilen ayar tanımlayıcıyla ilişkilendirilmiş değeri (dizge olarak) döndüren bir yönteme sahiptir.

// Java example
Properties config = new Properties();
config.load(...);
String valueStr = config.getProperty("listening-port");
// ...

// C# example
NameValueCollection setting = ConfigurationManager.AppSettings;
string valueStr = setting["listening-port"];
// ...

Her iki durumda da, yapılandırma dosyasından yüklenen dizeleri ayrıştırmalı ve dönüştürülen değerleri ilgili yazılan nesnelere atamalıyız (bu aşamada ayrıştırma hataları oluşabilir). Ayrıştırma adımından sonra, ayar değerlerinin belirli bir geçerlilik alanına ait olduğunu kontrol etmeliyiz: örneğin, bir sıranın maksimum boyutu pozitif bir değer olmalı, bazı değerler ilişkili olabilir (örnek: min <max ), ve bunun gibi.

Uygulamanın, ayarları en kısa sürede başlatması gerektiğini varsayalım: başka bir deyişle, uygulamanın ilk yaptığı işlem ayarları yüklemektir. Ayarlar için geçersiz olan değerler otomatik olarak varsayılan değerlerle değiştirilmelidir: bu, ilgili bir ayar grubuna gelirse, bu ayarların tümü varsayılan değerlerle ayarlanır.

Bu işlemleri gerçekleştirmenin en kolay yolu, önce tüm ayarları ayrıştıran, sonra yüklenen değerleri kontrol eden ve son olarak varsayılan değerleri ayarlayan bir yöntem oluşturmaktır. Ancak bu yaklaşımı kullanırsanız bakım zordur: uygulamayı geliştirirken ayar sayısı arttıkça kodu güncellemek giderek zorlaşır.

Bu sorunu çözmek için, Şablon Yöntemi modelini şu şekilde kullanmayı düşündüm .

public abstract class Setting
{
    protected abstract bool TryParseValues();

    protected abstract bool CheckValues();

    public abstract void SetDefaultValues();

    /// <summary>
    /// Template Method
    /// </summary>
    public bool TrySetValuesOrDefault()
    {
        if (!TryParseValues() || !CheckValues())
        {
            // parsing error or domain error
            SetDefaultValues();
            return false;
        }
        return true;
    }
}

public class RangeSetting : Setting
{
    private string minStr, maxStr;
    private byte min, max;

    public RangeSetting(string minStr, maxStr)
    {
        this.minStr = minStr;
        this.maxStr = maxStr;
    }

    protected override bool TryParseValues()
    {
        return (byte.TryParse(minStr, out min)
            && byte.TryParse(maxStr, out max));
    }

    protected override bool CheckValues()
    {
        return (0 < min && min < max);
    }

    public override void SetDefaultValues()
    {
        min = 5;
        max = 10;
    }
}

Sorun, bu şekilde, her bir ayar için tek bir değer için bile yeni bir sınıf yaratmamız gerektiğidir. Bu tür bir sorunun başka çözümleri var mı?

Özetle:

  1. Kolay bakım: örneğin, bir veya daha fazla parametrenin eklenmesi.
  2. Genişletilebilirlik: uygulamanın ilk sürümü tek bir yapılandırma dosyasını okuyabilir, ancak daha sonraki sürümler çok kullanıcılı bir kurulum imkanı sağlayabilir (admin temel bir yapılandırma ayarlar, kullanıcılar yalnızca belirli ayarları ayarlayabilir, vb.).
  3. Nesneye yönelik tasarım.

Bir .properties dosyasının kullanımını önerenler için dosyayı geliştirme, test etme ve daha sonra üretim sırasında saklayacağınız yer, çünkü aynı yerde olmayacağını umuyorum. Daha sonra, çalışma zamanında ortamı tespit edemez ve uygulamanızın içinde kodlanmış konumlara sahip değilseniz, uygulamanın hangi konumla (dev, test veya prod) yeniden derlenmesi gerekir.

Yanıtlar:


8

Temel olarak harici konfigürasyon dosyası bir YAML belgesi olarak kodlanır. Bu daha sonra uygulama başlatılırken ayrıştırılır ve bir yapılandırma nesnesine eşlenir.

Nihai sonuç sağlam ve her şeyden önce yönetimi kolaydır.


7

Bunu iki açıdan ele alalım: yapılandırma değerlerini elde etmek için API ve depolama formatı. Sık sık ilişkilidirler, ancak bunları ayrı ayrı düşünmek faydalıdır.

Yapılandırma API'si

Şablon Yöntemi kalıbı çok geneldir, ancak gerçekten bu genelliğe ihtiyacınız olup olmadığını sorguluyorum. Her için bir sınıf gerekiyordu tip yapılandırma değerinin. Gerçekten bu kadar çeşit var mı? Sanırım sadece bir avuçla başa çıkabileceğini tahmin ediyorum: dizeler, ints, yüzen, boolean, ve enums. Bunlar göz önüne alındığında, Configüzerinde bir avuç yöntem olan bir sınıfa sahip olabilirsiniz :

int getInt(name, default, min, max)
float getFloat(name, default, min, max)
boolean getBoolean(name, default)
String getString(name, default)
<T extends Enum<T>> T getEnum(name, Class<T> enumClass, T default)

(Sanırım bu sonda jenerik var.)

Temel olarak, her yöntem, config dosyasındaki string değerinin ayrıştırılmasını ve hataları işlemeyi ve uygunsa varsayılan değeri döndürmeyi bilir. Sayısal değerler için aralık kontrolü muhtemelen yeterlidir. Bir Integer.MIN_VALUE, Integer.MAX_VALUE aralığı sağlamaya eşdeğer olacak aralık değerlerini dışlayan aşırı yüklemelere sahip olmak isteyebilirsiniz. Bir Enum, bir dizgiyi sabit bir dizgiye karşı doğrulamak için güvenli bir yöntemdir.

Birden fazla değer, birbiriyle ilişkili değerler, dinamik tablo aramaları vb. Gibi bunun üstesinden gelmediği bazı şeyler var. Bunlar için özel ayrıştırma ve doğrulama yordamları yazabilirsiniz, ancak bu çok karmaşık hale gelirse, sorgulamaya başlarım Bir config dosyası ile çok fazla şey yapmaya çalışıyorsanız.

Depolama biçimi

Java özellik dosyaları, tek tek anahtar / değer çiftlerini depolamak için iyi görünür ve yukarıda açıkladığım değer türlerini oldukça iyi destekler. XML veya JSON gibi diğer biçimleri de düşünebilirsiniz, ancak iç içe geçmiş veya yinelenen verileriniz olmadıkça bunlar büyük olasılıkla gözden kaçar. Bu noktada bir yapılandırma dosyasının ötesinde görünüyor.

Telastyn, serileştirilmiş nesnelerden bahsetti. Serileştirmenin zorlukları olmasına rağmen, bu bir olasılıktır. Metin değil ikilidir, bu yüzden değerleri görmek ve düzenlemek zordur. Serileştirme uyumluluğu ile ilgilenmelisiniz. Serileştirilmiş girişte değerler eksikse (örneğin, Config sınıfına bir alan eklediniz ve bunun eski serileştirilmiş bir formunu okuyorsunuz), yeni alanlar null / sıfır olarak başlatılıyor. Başka bir varsayılan değerin doldurulup doldurulmayacağını belirlemek için mantık yazmanız gerekir. Fakat sıfır, yapılandırma değerinin olmadığını mı gösteriyor yoksa sıfır olarak mı belirtildi? Şimdi bu mantığı ayıklaman gerekiyor. Son olarak (bunun bir sorun olup olmadığından emin değilsiniz) hala serileştirilmiş nesne akışındaki değerleri doğrulamanız gerekebilir. Kötü niyetli bir kullanıcının seri hale getirilmiş bir nesne akışını algılanamayan bir şekilde değiştirmesi mümkündür (uygunsuz olsa da).

Mümkünse özelliklere sadık söyleyebilirim.


2
Hey Stuart, seni burada görmek güzel. Stuarts'ın cevabını ekleyeceğim: Genalics'i güçlü bir şekilde yazmak için Tempalte fikrinizin Java'da çalışacağını düşünüyorum, bu nedenle bir seçenek olarak <T> Ayarını da kullanabilirsiniz.
Martijn Verburg

@StuartMarks: Eh, benim ilk fikir yazmak için sadece oldu Config: sizin önerdiği yaklaşım sınıfını ve kullanımı getInt(), getByte(), getBoolean()ben her şeyden önce değerleri okumak ve bir bayrağa her değeri ilişkilendirmek olabilir, vb .. Sürekli bu fikirle (örneğin, ayrıştırma hataları gibi seri kaldırma sırasında bir sorun oluştuğunda bu bayrak yanlıştır). Bundan sonra, yüklenen tüm değerler için bir doğrulama aşaması başlatabilir ve varsayılan değerleri ayarlayabilirim.
enzom83

2
Tüm detayları basitleştirmek için bir çeşit JAXB veya YAML yaklaşımını tercih ederim.
Gary Rowe

4

Nasıl yaptım:

Her şeyi varsayılan değerlere sıfırla.

Dosyayı ayrıştırın, değerleri giderken saklayın. Ayarlanan yerler, değerlerin kabul edilebilir olmasını sağlamaktan sorumludur, hatalı değerler göz ardı edilir (ve böylece varsayılan değeri korur.)


Bu da iyi bir fikir olabilir: ayarların değerlerini yükleyen bir sınıf sadece yapılandırma dosyasındaki değerleri yüklemekle ilgilenebilir, yani sorumluluğu yalnızca değerleri yükleyen kişi olabilir Yapılandırma dosyasından; bunun yerine her modül (bazı ayarları kullanan) değerleri doğrulama sorumluluğu üstlenir.
enzom83

2

Bu tür bir sorunun başka çözümleri var mı?

İhtiyacınız olan tek şey basit bir yapılandırma ise, bunun için düz eski bir sınıf yapmak isterim. Varsayılanları başlatır ve yerleşik serileştirme sınıfları aracılığıyla uygulama tarafından dosyadan yüklenebilir. Daha sonra uygulama, ihtiyacı olan şeylere dolaştırır. Ayrıştırma veya dönüşümle uğraşmak, yapılandırma dizeleriyle uğraşmak yok, çöp atmak yok. Ve bu yapılandırma yapar yolu içinde kodu o ön ayar sunucusundan veya yüklenen / kaydedilmesi gerekiyor senaryolar ve kullanım daha kolay bir şekilde sizin birim testlerinde kullanılmak daha kolay.


1
Ayrıştırma veya dönüşümle uğraşmak, yapılandırma dizeleriyle uğraşmak yok, çöp atmak yok. Ne demek istiyorsun?
enzom83

1
Demek istediğim: 1. AppConfig sonucunu (bir dize) alıp istediğiniz şekilde ayrıştırmanıza gerek yok. 2. İstediğiniz config parametresini seçmek için herhangi bir dizge belirtmeniz gerekmez; Bu, insan hatasına yatkın ve refactor için zor olan şeylerden biridir ve 3. değeri programlı olarak ayarlayacağınız zaman başka tip dönüşümler yapmanız gerekmez.
Telastyn

2

En azından. NET'te kolayca yazılmış kendi yapılandırma nesnelerinizi kolayca oluşturabilirsiniz - hızlı bir örnek için bu MSDN makalesine bakın .

Protip: config sınıfınızı bir arayüze sarın ve uygulamanızın bununla konuşmasına izin verin. Testler veya kar için sahte konfigürasyonun enjekte edilmesini kolaylaştırır.


MSDN makalesini okudum: ilginç, aslında ConfigurationElementsınıfın her bir alt sınıfı bir değer grubunu temsil edebilir ve herhangi bir değer için bir doğrulayıcı belirtebilirsiniz. Ancak, örneğin, dört olasılıktan oluşan bir konfigürasyon elemanını temsil etmek istiyorsam, toplamın 1'e eşit olması gerektiği için dört olasılık değeri ilişkilendirilir, çünkü bu konfigürasyon elemanını nasıl doğrularım?
enzom83

1
Genelde bunun düşük seviye konfigürasyon doğrulama için bir şey olmadığını iddia ediyordum - kodlarımı içerecek şekilde konfigürasyon sınıfıma bir AssertConfigrationIsValid metodu eklerdim. Bu sizin için işe yaramazsa, özniteliğin temel sınıfını genişleterek kendi yapılandırma doğrulayıcılarınızı oluşturabileceğinizi düşünüyorum. Karşılaştırma doğrulayıcısına sahipler, bu yüzden açıkça mülkler arası konuşabiliyorlar.
Wyatt Barnett
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.