Prosty sposób na zachowanie ustawień aplikacji Java jest reprezentowany przez plik tekstowy z rozszerzeniem „.properties” zawierający identyfikator każdego ustawienia powiązanego z określoną wartością (ta wartość może być liczbą, łańcuchem, datą itp.) . C # stosuje podobne podejście, ale plik tekstowy musi mieć nazwę „App.config”. W obu przypadkach w kodzie źródłowym należy zainicjować określoną klasę do odczytu ustawień: ta klasa ma metodę, która zwraca wartość (jako ciąg) powiązaną z określonym identyfikatorem ustawienia.
// 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"];
// ...
W obu przypadkach powinniśmy przeanalizować ciągi znaków załadowane z pliku konfiguracyjnego i przypisać przekonwertowane wartości do powiązanych obiektów tekstowych (podczas tej fazy mogą wystąpić błędy analizy). Po etapie analizy musimy sprawdzić, czy wartości ustawień należą do konkretnej dziedziny ważności: na przykład maksymalny rozmiar kolejki powinien być wartością dodatnią, niektóre wartości mogą być powiązane (przykład: min <max ), i tak dalej.
Załóżmy, że aplikacja powinna załadować ustawienia, jak tylko się uruchomi: innymi słowy, pierwszą operacją wykonywaną przez aplikację jest załadowanie ustawień. Wszelkie nieprawidłowe wartości ustawień muszą zostać automatycznie zastąpione wartościami domyślnymi: jeśli tak się stanie z grupą powiązanych ustawień, wszystkie te ustawienia zostaną ustawione na wartości domyślne.
Najłatwiejszym sposobem na wykonanie tych operacji jest stworzenie metody, która najpierw analizuje wszystkie ustawienia, a następnie sprawdza załadowane wartości i ostatecznie ustawia wartości domyślne. Jednak konserwacja jest trudna, jeśli zastosujesz to podejście: wraz ze wzrostem liczby ustawień podczas opracowywania aplikacji coraz trudniej jest zaktualizować kod.
Aby rozwiązać ten problem, pomyślałem o użyciu wzorca metody szablonu w następujący sposób.
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;
}
}
Problem polega na tym, że w ten sposób musimy stworzyć nową klasę dla każdego ustawienia, nawet dla pojedynczej wartości. Czy istnieją inne rozwiązania tego rodzaju problemu?
W podsumowaniu:
- Łatwa konserwacja: na przykład dodanie jednego lub więcej parametrów.
- Rozszerzalność: pierwsza wersja aplikacji może odczytać pojedynczy plik konfiguracyjny, ale późniejsze wersje mogą dawać możliwość konfiguracji dla wielu użytkowników (administrator konfiguruje konfigurację podstawową, użytkownicy mogą konfigurować tylko niektóre ustawienia itp.).
- Projektowanie obiektowe.
źródło
Odpowiedzi:
Zasadniczo zewnętrzny plik konfiguracyjny jest zakodowany jako dokument YAML. Jest to następnie analizowane podczas uruchamiania aplikacji i mapowane na obiekt konfiguracyjny.
Ostateczny wynik jest solidny i przede wszystkim prosty w zarządzaniu.
źródło
Rozważmy to z dwóch punktów widzenia: API, aby uzyskać wartości konfiguracyjne, i formatu przechowywania. Często są ze sobą powiązane, ale warto rozważyć je oddzielnie.
Interfejs API konfiguracji
Wzorzec metody szablonów jest bardzo ogólny, ale wątpię, czy naprawdę potrzebujesz tej ogólności. Potrzebujesz klasy dla każdego typu wartości konfiguracji. Czy naprawdę masz tyle rodzajów? Sądzę, że można sobie z tym poradzić za pomocą tylko garstki: ciągów, liczb całkowitych, liczb zmiennoprzecinkowych, boolean i wyliczeń. Biorąc to pod uwagę, możesz mieć
Config
klasę, która ma garść metod:(Myślę, że mam generyki w tym ostatnim).)
Zasadniczo każda metoda wie, jak obsługiwać parsowanie wartości ciągu z pliku konfiguracyjnego, obsługiwać błędy i w razie potrzeby zwracać wartość domyślną. Prawdopodobnie wystarcza sprawdzenie zakresu wartości liczbowych. Możesz chcieć mieć przeciążenia, które pomijają wartości zakresu, co byłoby równoważne z podaniem zakresu Integer.MIN_VALUE, Integer.MAX_VALUE. Enum to bezpieczny sposób sprawdzania poprawności łańcucha znaków względem ustalonego zestawu łańcuchów.
Są pewne rzeczy, których to nie obsługuje, takie jak wiele wartości, wartości, które są ze sobą powiązane, dynamiczne wyszukiwanie tabel itp. Możesz napisać dla nich specjalne procedury analizowania i sprawdzania poprawności, ale jeśli będzie to zbyt skomplikowane, zacznę zadawać pytania czy próbujesz zrobić zbyt wiele z plikiem konfiguracyjnym.
Format przechowywania
Pliki właściwości Java wydają się dobrze do przechowywania pojedynczych par klucz-wartość i całkiem dobrze obsługują typy wartości, które opisałem powyżej. Możesz również wziąć pod uwagę inne formaty, takie jak XML lub JSON, ale prawdopodobnie są one nadmierne, chyba że zagnieżdżono lub powtórzono dane. W tym momencie wydaje się, że jest to coś więcej niż plik konfiguracyjny ....
Telastyn wspomniał o serializowanych obiektach. Jest to możliwe, chociaż serializacja ma pewne trudności. Jest binarny, a nie tekstowy, więc trudno jest zobaczyć i edytować wartości. Musisz poradzić sobie ze zgodnością serializacji. Jeśli brakuje wartości w serializowanym wejściu (np. Dodałeś pole do klasy Config i czytasz starą postać zserializowaną), nowe pola są inicjowane na zero / zero. Musisz napisać logikę, aby ustalić, czy wypełnić jakąś inną wartość domyślną. Ale czy zero wskazuje na brak wartości konfiguracji, czy też określono ją jako zero? Teraz musisz debugować tę logikę. Wreszcie (nie jestem pewien, czy to dotyczy) nadal może być konieczne sprawdzenie poprawności wartości w serializowanym strumieniu obiektów. Złośliwy użytkownik może (choć jest niewygodny) modyfikować nieoczekiwanie zmodyfikowany szereg obiektów.
Powiedziałbym, żeby trzymać się właściwości, jeśli to w ogóle możliwe.
źródło
Config
klasę i stosowanie metody zaproponowanej przez Ciebie:getInt()
,getByte()
,getBoolean()
, etc .. Kontynuując tę ideę, po raz pierwszy przeczytałem wszystkie wartości i mogłem skojarzyć każdą wartość z flagą (ta flaga jest fałszywa, jeśli wystąpił problem podczas deserializacji, na przykład błędy parsowania). Następnie mogłem rozpocząć fazę sprawdzania poprawności wszystkich załadowanych wartości i ustawić dowolne wartości domyślne.Jak to zrobiłem:
Zainicjuj wszystko do wartości domyślnych.
Analizuj plik, przechowując wartości w miarę upływu czasu. Ustawiane miejsca są odpowiedzialne za zapewnienie akceptowalności wartości, złe wartości są ignorowane (a zatem zachowują wartość domyślną).
źródło
Jeśli wszystko, czego potrzebujesz, to prosta konfiguracja, lubię tworzyć dla niej zwykłą starą klasę. Inicjuje wartości domyślne i może zostać załadowany z pliku przez aplikację za pośrednictwem wbudowanych klas serializacji. Następnie aplikacja przekazuje je do potrzebnych rzeczy. Żadnych rozmyślań podczas analizowania lub konwersji, bez przekręcania ciągów konfiguracji, bez wyrzucania śmieci. I to sprawia, że konfiguracja sposób łatwiejszy w obsłudze dla w-kodu scenariuszach, w których musi być zapisywane / odczytywane z serwerem lub jako zaprogramowane i sposób łatwiejszy do wykorzystania w testach jednostkowych.
źródło
Przynajmniej w .NET można dość łatwo tworzyć własne, mocno wpisane obiekty konfiguracyjne - szybki przykład można znaleźć w tym artykule MSDN .
Protip: umieść klasę config w interfejsie i pozwól swojej aplikacji na to porozmawiać. Ułatwia wstrzykiwanie fałszywej konfiguracji do testów lub dla zysku.
źródło
ConfigurationElement
klasy może reprezentować grupę wartości, a dla każdej wartości można określić walidator. Ale jeśli na przykład chciałbym przedstawić element konfiguracji, który składa się z czterech prawdopodobieństw, cztery wartości prawdopodobieństwa są skorelowane, ponieważ ich suma musi być równa 1. Jak zweryfikować ten element konfiguracji?