이것은 실제로 매우 중요한 질문이며 거의 모든 응용 프로그램의 핵심 부분이지만 충분한 중요성을 부여받지 못하기 때문에 종종 잘못 수행됩니다. 내 지침은 다음과 같습니다.
모든 설정을 포함하는 구성 클래스는 평범한 오래된 데이터 유형 struct / class이어야합니다.
class Config {
int prop1;
float prop2;
SubConfig subConfig;
}
메소드를 가질 필요는 없으며 상속을 포함해서는 안됩니다 (변형 필드를 구현하기 위해 귀하의 언어로 유일하게 선택하지 않는 한-다음 단락 참조). 구성을 사용하여 설정을 더 작은 특정 구성 클래스 (예 : 위의 subConfig)로 그룹화 할 수 있습니다. 이 방법을 사용하면 최소한의 종속성이 있으므로 단위 테스트 및 응용 프로그램에서 전달하는 것이 이상적입니다.
다른 설정에 대한 구성이 구조가 다른 경우 변형 유형을 사용해야 할 수 있습니다. 올바른 (하위) 구성 클래스로 캐스트하기 위해 값을 읽을 때 특정 시점에 동적 캐스트를 넣어야한다는 것은 인정되며, 이는 다른 구성 설정에 따라 달라집니다.
다음을 수행하여 모든 설정을 필드로 입력하는 것에 대해 게으르지 않아야합니다.
class Config {
Dictionary<string, string> values;
};
다루고있는 필드를 알 필요가없는 일반화 된 직렬화 클래스를 작성할 수 있다는 의미에서 유혹적입니다. 그러나 잘못되어 잠시 이유를 설명하겠습니다.
구성의 직렬화는 완전히 별도의 클래스에서 수행됩니다. 이 작업을 수행하는 데 사용하는 API 또는 라이브러리에 관계없이 직렬화 함수의 본문에는 기본적으로 파일의 경로 / 키에서 객체의 필드로의 맵에 해당하는 항목이 포함되어야합니다. 일부 언어는 좋은 내성을 제공하며 즉시 사용할 수 있으며, 다른 언어는 매핑을 명시 적으로 작성해야하지만 중요한 것은 매핑을 한 번만 작성하면됩니다. 예를 들어 c ++ 부스트 프로그램 옵션 파서 문서에서 수정 한이 추출을 고려하십시오.
struct Config {
int opt;
} conf;
po::options_description desc("Allowed options");
desc.add_options()
("optimization", po::value<int>(&conf.opt)->default_value(10);
마지막 줄에는 기본적으로 "최적화"가 Config :: opt에 매핑되고 예상되는 형식의 선언이 있습니다. 파일의 매개 변수가 실제로 float 또는 int가 아니거나 존재하지 않는 경우 유형이 예상 한 것이 아닌 경우 구성 읽기가 실패하려고합니다. 즉, 파일 형식 / 확인에 문제가 있기 때문에 파일을 읽을 때 오류가 발생해야하며 예외 / 반환 코드를 던져 정확한 문제를보고해야합니다. 나중에 프로그램에서 이것을 지연 시켜서는 안됩니다. 그렇기 때문에 파일을 읽을 때 실패하지 않는 위에서 언급 한대로 모든 사전 스타일 구성 요소를 잡으려는 유혹을받지 않아야합니다. 값이 필요할 때까지 캐스팅이 지연됩니다.
Config 클래스는 어떤 방식 으로든 읽기 전용으로 만들어야합니다. 클래스를 작성할 때 클래스의 컨텐츠를 한 번 설정하고 파일에서 초기화해야합니다. 변경되지 않는 const 설정뿐만 아니라 변경되는 응용 프로그램에서 동적 설정이 필요한 경우 구성 클래스의 비트가 읽기 전용이 아닌 동적 클래스를 처리하는 별도의 클래스가 있어야합니다 .
프로그램의 한 위치에서 파일을 읽는 것이 이상적입니다. 즉, " ConfigReader
" 인스턴스가 하나만 있습니다 . 그러나 Config 인스턴스를 필요한 곳으로 전달하는 데 어려움을 겪고 있다면 전역 구성을 도입하는 것보다 두 번째 ConfigReader를 사용하는 것이 좋습니다 (이것은 OP가 의미하는 것입니다 "정적" "), 다음으로 넘어갑니다.
싱글 톤의 매혹적인 사이렌 노래를 피하십시오. "클래스 클래스를 통과해야하는 것을 막아 줄 것입니다. 모든 생성자는 아름답고 깨끗합니다. 계속해보세요." 진실은 잘 설계된 테스트 가능한 아키텍처로 구성 클래스 또는 그 일부를 응용 프로그램의 많은 클래스를 통해 전달할 필요가 거의 없습니다. 최상위 클래스, main () 함수 또는 무엇이든 찾을 수있는 것은 개별 값으로 conf를 풀고 구성 요소 클래스에 인수로 제공 한 인수 (수동 종속성) 주입). 싱글 톤 / 글로벌 / 정적 conf는 애플리케이션 테스트를 구현하고 이해하기 훨씬 어렵게 만듭니다. 예를 들어, 새로운 개발자가 팀을 구성하기 위해 글로벌 상태를 설정해야한다는 것을 모르는 새로운 개발자를 혼란스럽게 할 것입니다.
언어가 속성을 지원하는 경우 이러한 목적으로 사용해야합니다. 그 이유는 하나 이상의 다른 설정에 따라 파생 된 구성 설정을 매우 쉽게 추가 할 수 있기 때문입니다. 예 :
int Prop1 { get; }
int Prop2 { get; }
int Prop3 { get { return Prop1*Prop2; }
언어가 속성 관용구를 기본적으로 지원하지 않는 경우 동일한 효과를 얻는 해결 방법이 있거나 보너스 설정을 제공하는 래퍼 클래스를 만들면됩니다. 그렇지 않으면 속성의 이점을 부여 할 수 없다면 수동으로 작성하고 일부 OO 신을 기쁘게하기 위해 게터 / 세터를 사용하는 것이 시간 낭비입니다. 평범한 낡은 밭에서 더 나아질 것입니다.
우선 순위에 따라 서로 다른 위치에서 여러 구성을 병합하고 가져 오는 시스템이 필요할 수 있습니다. 우선 순위는 잘 정의되어 있고 모든 개발자 / 사용자가 이해해야합니다 (예 : Windows 레지스트리 HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE 고려). 이 기능 스타일을 수행하여 구성을 읽기 전용으로 유지할 수 있습니다.
final_conf = merge(user_conf, machine_conf)
오히려
conf.update(user_conf)
선택한 프레임 워크 / 언어가 자체적으로 잘 알려진 기본 제공 구성 메커니즘을 제공하는 경우 자신의 롤링 대신이를 사용하는 이점을 고려해야합니다.
그래서. 고려해야 할 많은 측면-올바르게 이해하면 응용 프로그램 아키텍처에 크게 영향을 미쳐 버그를 줄이며 쉽게 테스트 할 수 있으며 다른 곳에서 좋은 디자인을 사용해야합니다.