응용 프로그램 설정을로드하는 가장 좋은 방법


24

Java 애플리케이션의 설정을 유지하는 간단한 방법은 특정 값과 연관된 각 설정의 ID를 포함하는 ".properties"확장자를 가진 텍스트 파일로 표시됩니다 (이 값은 숫자, 문자열, 날짜 등일 수 있음). . C #은 비슷한 방법을 사용하지만 텍스트 파일의 이름은 "App.config"여야합니다. 두 경우 모두 소스 코드에서 설정을 읽기 위해 특정 클래스를 초기화해야합니다.이 클래스에는 지정된 설정 식별자와 연관된 값 (문자열)을 반환하는 메서드가 있습니다.

// 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"];
// ...

두 경우 모두 구성 파일에서로드 된 문자열을 구문 분석하고 변환 된 값을 관련 유형이 지정된 객체에 할당해야합니다 (이 단계에서 구문 분석 오류가 발생할 수 있음). 구문 분석 단계 후 설정 값이 특정 유효 도메인에 속하는지 확인해야합니다. 예를 들어 대기열의 최대 크기는 양수 값이어야하며 일부 값은 관련이있을 수 있습니다 (예 : min <max ), 등등.

응용 프로그램이 시작하자마자 설정을로드해야한다고 가정합니다. 즉, 응용 프로그램이 수행하는 첫 번째 작업은 설정을로드하는 것입니다. 설정에 대한 유효하지 않은 값은 기본값으로 자동 대체되어야합니다. 이것이 관련 설정 그룹에 발생하면 해당 설정은 모두 기본값으로 설정됩니다.

이러한 작업을 수행하는 가장 쉬운 방법은 먼저 모든 설정을 구문 분석 한 다음로드 된 값을 확인하고 마지막으로 기본값을 설정하는 방법을 만드는 것입니다. 그러나이 방법을 사용하면 유지 관리가 어려워집니다. 응용 프로그램을 개발하는 동안 설정 수가 늘어 나면 코드를 업데이트하기가 점점 어려워집니다.

이 문제를 해결하기 위해 다음과 같이 템플릿 방법 패턴 을 사용하려고 생각했습니다 .

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;
    }
}

문제는 이런 식으로 단일 값에 대해서도 각 설정에 대해 새 클래스를 만들어야한다는 것입니다. 이런 종류의 문제에 대한 다른 해결책이 있습니까?

요약해서 말하자면:

  1. 손쉬운 유지 보수 : 예를 들어 하나 이상의 매개 변수 추가.
  2. 확장 성 : 응용 프로그램의 첫 번째 버전은 단일 구성 파일을 읽을 수 있지만 이후 버전은 다중 사용자 설정 가능성을 제공 할 수 있습니다 (관리자가 기본 구성을 설정하고 사용자는 특정 설정 만 설정할 수 있음 등).
  3. 객체 지향 디자인.

.properties 파일의 사용을 제안하는 사람들은 개발, 테스트 및 생산 과정에서 파일 자체를 저장하는 위치가 같기 때문에 같은 위치에 있지 않기를 바랍니다. 그런 다음 런타임에 환경을 감지하고 앱 내부에 하드 코딩 된 위치를 가질 수 없다면 응용 프로그램을 어떤 위치 (dev, test 또는 prod)로 다시 컴파일해야합니다.

답변:


8

기본적으로 외부 구성 파일은 YAML 문서로 인코딩됩니다. 그런 다음 응용 프로그램을 시작하는 동안 구문 분석되어 구성 개체에 매핑됩니다.

최종 결과는 강력하고 무엇보다 관리가 간단합니다.


7

구성 값을 가져 오는 API와 저장소 형식의 두 가지 관점에서이를 고려해 보겠습니다. 그들은 종종 관련이 있지만 별도로 고려하는 것이 좋습니다.

구성 API

템플릿 방법 패턴은 매우 일반적이지만 그 일반성이 실제로 필요한지 의문입니다. 각 유형 의 구성 값에 대한 클래스가 필요 합니다. 정말 많은 유형이 있습니까? 문자열, int, float, boolean 및 enum과 같은 소수만으로도 얻을 수 있다고 생각합니다. 이것들을 감안할 때 Config몇 가지 방법 이있는 클래스를 가질 수 있습니다 .

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)

(나는 그 마지막에 제네릭을 얻었습니다.)

기본적으로 각 메소드는 구성 파일에서 문자열 값의 구문 분석을 처리하고 오류를 처리하고 적절한 경우 기본값을 리턴하는 방법을 알고 있습니다. 숫자 값에 대한 범위 확인이면 충분합니다. 범위 값을 생략하는 오버로드를 원할 수 있으며 이는 Integer.MIN_VALUE, Integer.MAX_VALUE 범위를 제공하는 것과 같습니다. 열거 형은 고정 된 문자열 집합에 대해 문자열의 유효성을 검사하는 안전한 형식의 방법입니다.

여러 값, 상호 관련된 값, 동적 테이블 조회 등과 같이 처리하지 못하는 몇 가지가 있습니다. 특수 구문 분석 및 유효성 검사 루틴을 작성할 수는 있지만 너무 복잡하면 질문을 시작합니다. 구성 파일로 너무 많은 일을하려고하는지 여부.

저장 형식

Java 특성 파일은 개별 키-값 쌍을 저장하는 데 적합하며 위에서 설명한 값 유형을 지원합니다. XML 또는 JSON과 같은 다른 형식을 고려할 수도 있지만, 데이터를 중첩하거나 반복하지 않는 한 이러한 형식은 너무 과도합니다. 그 시점에서는 구성 파일 너머로 보입니다 ....

Telastyn은 직렬화 된 객체를 언급했습니다. 직렬화에는 어려움이 있지만 이것은 가능합니다. 텍스트가 아니라 이진이므로 값을보고 편집하기가 어렵습니다. 직렬화 호환성을 처리해야합니다. 직렬화 된 입력에서 값이 누락 된 경우 (예 : Config 클래스에 필드를 추가하고 이전 직렬화 된 형식을 읽는 경우) 새 필드는 null / zero로 초기화됩니다. 다른 기본값을 채울지 여부를 결정하려면 논리를 작성해야합니다. 그러나 0은 구성 값이 없음을 나타내거나 0으로 지정 되었습니까? 이제이 로직을 디버깅해야합니다. 마지막으로 (이것이 문제인지 확실하지 않은 경우) 직렬화 된 객체 스트림에서 값의 유효성을 검사해야 할 수도 있습니다. 악의적 인 사용자가 직렬화 된 객체 스트림을 감지 할 수 없도록 수정할 수 있습니다 (불편하지만).

가능하다면 속성을 고수한다고 말하고 싶습니다.


2
안녕 스튜어트, 반가워요 :-). 나는 스튜어트의 답변에 제네릭을 사용하여 강력한 타이핑을한다면 당신의 템퍼 트 아이디어가 자바에서 작동 할 것이라고 생각합니다. 따라서 Setting <T>를 옵션으로 가질 수 있습니다.
Martijn Verburg

@StuartMarks : 글쎄, 내 첫번째 생각은 쓰기 만했다 Config: 당신에 의해 제안 된 접근 방식 클래스를 사용할 getInt(), getByte(), getBoolean()내가 먼저 모든 값을 읽고 나는 플래그에 각 값을 연결할 수, 등 계속이 아이디어를 역 직렬화 중에 문제가 발생한 경우 (예 : 구문 분석 오류)이 플래그는 false입니다. 그 후, 모든로드 된 값에 대한 유효성 검사 단계를 시작하고 기본값을 설정할 수 있습니다.
enzom83

2
모든 세부 사항을 단순화하기 위해 일종의 JAXB 또는 YAML 방식을 선호합니다.
게리 로우

4

내가 한 방법 :

모든 것을 기본값으로 초기화하십시오.

값을 저장하면서 파일을 구문 분석하십시오. 설정되는 장소는 값을 허용하고 잘못된 값은 무시하므로 기본값을 유지해야합니다.


이것은 또한 좋은 아이디어 일 수 있습니다 : 설정의 값을로드하는 클래스는 구성 파일에서 값을로드하기 위해 처리해야 할 수도 있습니다. 즉, 책임은 값을로드하는 책임 만 할 수 있습니다. 구성 파일에서; 대신 각 모듈 (일부 설정 사용)은 값을 검증 할 책임이 있습니다.
enzom83

2

이런 종류의 문제에 대한 다른 해결책이 있습니까?

당신이 필요로하는 모든 것이 간단한 구성이라면, 나는 그것을 위해 오래된 클래스를 만들고 싶습니다. 기본값을 초기화하고 내장 직렬화 클래스를 통해 앱이 파일에서로드 할 수 있습니다. 그런 다음 앱은 필요한 것을 전달합니다. 구문 분석이나 변환에 대해 고민하거나 구성 문자열로 조이는 일이 없으며 캐스팅 쓰레기가 없습니다. 그리고 구성 만드는 방법 에 코드가 사전 설정 서버 또는로로드 / 저장해야 시나리오 및 사용하기 쉬운 방법으로 단위 테스트에서 사용하기에 용이.


1
구문 분석이나 변환에 대해 고민하거나 구성 문자열로 조이는 일이 없으며 캐스팅 쓰레기가 없습니다. 무슨 소리 야?
enzom83

1
1. AppConfig 결과 (문자열)를 가져 와서 원하는 것으로 구문 분석 할 필요는 없습니다. 2. 원하는 구성 매개 변수를 선택하기 위해 문자열을 지정할 필요가 없습니다. 이는 인적 오류가 발생하기 쉽고 리팩토링하기 어려운 것들 중 하나입니다. 3. 프로그래밍 방식으로 값을 설정할 때 다른 유형 변환을 수행 할 필요가 없습니다.
Telastyn

2

최소한 .NET에서는 강력한 형식의 구성 개체를 쉽게 쉽게 만들 수 있습니다 . 빠른 예제는 이 MSDN 문서 를 참조하십시오 .

팁 : 구성 클래스를 인터페이스로 감싸고 응용 프로그램이 대화하도록하십시오. 테스트 또는 이익을 위해 가짜 구성을 쉽게 주입 할 수 있습니다.


MSDN 기사를 읽었습니다. 흥미 롭습니다. 본질적으로 클래스의 각 하위 ConfigurationElement클래스는 값 그룹을 나타낼 수 있으며 모든 값에 대해 유효성 검사기를 지정할 수 있습니다. 그러나 예를 들어 4 개의 확률로 구성된 구성 요소를 나타내려면 합계가 1과 같아야하기 때문에 4 개의 확률 값은 서로 관련이 있습니다.이 구성 요소의 유효성을 어떻게 검사합니까?
enzom83

1
나는 일반적으로 저수준 구성 검증을위한 것이 아니라고 주장합니다 .AssertConfigrationIsValid 메소드를 구성 클래스에 추가하여이를 코드로 다루겠습니다. 그것이 효과가 없다면 속성의 기본 클래스를 확장하여 자체 구성 검사기를 만들 수 있다고 생각합니다. 그들은 비교 유효성 검사기를 가지고 있으므로 분명히 교차 속성을 말할 수 있습니다.
Wyatt Barnett
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.