사용자 정의 설정을 통해 플러그인 디자인을 실현하다

소프트웨어 디자인에는 약속이 설정보다 낫다는 말이 있는데 많은 사람들이 이를 설정을 거부하는 이유로 삼는다.그러나 약속과 설정의 사용은 어느 정도 문제가 있다.나는 이른바 확장성을 위해 너의 응용을 위해 너 자신만이 이해할 수 있는 설정 체계를 설계하는 것에 찬성하지 않는다.그러나 많은 장면에서 설정은 응용의 유연성을 제공하는 가장 중요하고 심지어 유일한 경로이다.프레임 디자이너에게 있어서 배치에 대한 통제는 기본적인 기술이다.
사용자 정의 설정을 거의 사용하지 않을 수도 있습니다. 아마도 사용자 정의 설정은 App Setting에만 한정되어 있을 것입니다. 그러나 시스템 설정에 대해서만 이해해야 할 것 같습니다.Configuration이라는 이름 공간 아래의 몇 가지 기본적인 유형은 기본적으로 알고 있습니다.예를 들어 ConfigurationSection, ConfigurationElement, ConfigurationElementCollection 등이다.이 기사는 System에 대해 소개하지 않습니다.Configuration의 기초 지식은 간단한 예를 통해 이른바 고급 지식점, 예를 들어 식별할 수 없는 설정 요소의 동적 해석 등을 설명한다.(소스 코드는 여기서 다운로드)
디렉터리 1, 사용자 정의 설정을 통해 이루어진 최종 효과 2, 관련 설정 유형의 정의 3, 두 가지 중요한 유형: NameTypeConfiguration Element과 NameTypeConfiguration Element CollectionT 4, ResourceProvider Factory의 정의 5, 보충
1. 사용자 정의 설정을 통해 이루어진 최종 효과
사용자 정의 설정의 역할에 대한 깊은 이미지를 만들기 위해 간단한 예를 보여 드리겠습니다..NET의 자원은.resx 파일에 국한되지 않습니다. 임의의 저장 형식에 소개된 사용자 정의 ResourceManager를 사용하면 다양한 자원 저장 형식에 대한 지원을 실현할 수 있습니다.현재 자원과의 읽기만 주목하고 우리는 서로 다른 저장 형식의 자원 읽기 조작을 바탕으로 해당하는 ResourceProovider에서 다음과 같은 간단한 IResourceProvider 인터페이스를 실현할 것이다.
   
   
   
   
1 : public interface IResourceProvider
2 : {
3 : object GetObject( string key);
4 : }

그리고 우리는 두 개의 구체적인 ResourceProvider: DbResourceProvider와 XmlResourceProvider를 만들었는데 각각 데이터베이스 테이블과 XML 파일의 자원 저장 형식을 바탕으로 한다.DbResourceProvider는 데이터베이스에 연결되고 설정된 연결 문자열을 인용해야 하기 때문에 ConnectionStringName 속성이 있습니다.XmlResourceProvider는 파일 경로를 나타내는 특정 XML 파일에 액세스해야 합니다. FileName 속성은 파일 경로를 나타냅니다.
   
   
   
   
1 : [ConfigurationElementType( typeof (DbResourceProviderConfigurationElement))]
2 : public class DbResourceProvider : IResourceProvider
3 : {
4 : public string ConnnectionStringName { get ; private set ; }
5 : public DbResourceProvider( string connectionStringName)
6 : {
7 : this .ConnnectionStringName = connectionStringName;
8 : }
9 : public object GetObject( string key)
10 : {
11 : throw new NotImplementedException();
12 : }
13 : public override string ToString()
14 : {
15 : return string .Format( " {0}
\tConncectionString Name:{1}
" , typeof (DbResourceProvider).FullName, this .ConnnectionStringName);
16 : }
17 : }
18 :
19 : [ConfigurationElementType( typeof (XmlResourceProviderConfigurationElement))]
20 : public class XmlResourceProvider : IResourceProvider
21 : {
22 : public string FileName { get ; private set ; }
23 : public XmlResourceProvider( string fileName)
24 : {
25 : this .FileName = fileName;
26 : }
27 : public object GetObject( string key)
28 : {
29 : throw new NotImplementedException();
30 : }
31 : public override string ToString()
32 : {
33 : return string .Format( " {0}
\tFile Name:{1}
" , typeof (XmlResourceProvider).FullName, this .FileName);
34 : }
35 : }

어떤 ResourceProvider를 사용할지 구성을 통해 결정됩니다.전체 구성은artech에 정의됩니다.resources 프로필에서, 이 프로필은providers 서브 노드를 가지고 있으며, 일련의 ResourceProvider 목록을 정의합니다.모든 ResourceProvider 설정은 두 가지 같은 속성을 가지고 있습니다: Name과 Type, 그리고 자신만의 설정 속성 (예를 들어 DbResourceProvider의connectionStringName, XmlResourceProvider의 fileName).기본적으로 어떤 Provider를 사용할지 설정절의defaultProvider 속성을 통해 결정합니다.이 예에서는 기본적으로 DbProvider를 사용합니다.
   
   
   
   
1 : ? xml version = " 1.0 " encoding = " utf-8 " ?
2 : configuration
3 : configSections
4 : section name = " artech.resources " type = " Artech.Resources.Configuration.ResourceSettings,Artech.CustomConfiguration " /
5 : / configSections
6 : artech.resources defaultProvider = " DbProvider "
7 : providers
8 : add name = " DbProvider " type = " Artech.Resources.DbResourceProvider, Artech.CustomConfiguration " connectionStringName = " LocalSqlServer " /
9 : add name = " XmlProvider " type = " Artech.Resources.XmlResourceProvider, Artech.CustomConfiguration " fileName = " C:\resources.xml " /
10 : / providers
11 : / artech.resources
12 : / configuration

현재 ResourceProviderFactory의 공장 클래스가 있습니다. 설정에 따라 기본 ResourceProvider를 만들거나 지정한 이름의 ResourceProvider를 만들 수 있습니다.이제 ResourceProviderFactory는 다음과 같은 방법으로 사용됩니다.
   
   
   
   
1 : static void Main( string [] args)
2 : {
3 : IResourceProvider resourceProvider = ResourceProviderFactory.GetResourceProvider();
4 : Console.WriteLine(resourceProvider);
5 : Console.WriteLine();
6 :
7 : resourceProvider = ResourceProviderFactory.GetResourceProvider( " XmlProvider " );
8 : Console.WriteLine(resourceProvider);
9 : Console.WriteLine();
10 :
11 : resourceProvider = ResourceProviderFactory.GetResourceProvider( " DbProvider " );
12 : Console.WriteLine(resourceProvider);
13 : Console.WriteLine();
14 : }

결과 출력:
   
   
   
   
1 : Artech.Resources.DbResourceProvider
2 : ConncectionString Name:LocalSqlServer
3 :
4 : Artech.Resources.XmlResourceProvider
5 : File Name:C:\resources.xml
6 :
7 : Artech.Resources.DbResourceProvider
8 : ConncectionString Name:LocalSqlServer

다음은 전체 설정 체계와 ResourceProviderFactory의 실현을 소개합니다.
2. 관련 구성 유형의 정의
이제 설정과 관련된 유형의 정의를 살펴봅시다.전체 구성 섹션은 ConfigurationSection에서 직접 상속되는 다음과 같은 ResourceSettings 클래스로 정의됩니다.ResourceSettings는 두 가지 설정 속성을 가지고 있는데 그것이 바로DefaultProvider와Providers이다. 각각artech를 대표한다.resources의defaultProvider 속성과providers 하위 노드입니다.
   
   
   
   
1 : public class ResourceSettings: ConfigurationSection
2 : {
3 : [ConfigurationProperty( " defaultProvider " , IsRequired = true )]
4 : public string DefaultProvider
5 : {
6 : get { return ( string ) this [ " defaultProvider " ];}
7 : set { this [ " defaultProvider " ] = value;}
8 : }
9 : [ConfigurationProperty( " providers " , IsRequired = true )]
10 : public NameTypeElementCollectionResourceProviderConfigurationElement Providers
11 : {
12 : get { return (NameTypeElementCollectionResourceProviderConfigurationElement) this [ " providers " ];}
13 : set { this [ " providers " ] = value;}
14 : }
15 : public static ResourceSettings GetConfiguration()
16 : {
17 : return (ResourceSettings)ConfigurationManager.GetSection( " artech.resources " );
18 : }
19 : }

속성Providers는 NameTypeElementCollectionT라는 일반 유형입니다.이름에서 알 수 있듯이 이것은 설정된 ResourceProvider 집합을 대표하는 집합 유형입니다.ResourceProvider 기반 구성 정의는 다음과 같은 ResourceProvider Configuration Element 추상 클래스에 있습니다.이 클래스는 사용자가 정의한 NameType Configuration Element 유형을 계승하고, 해당하는 ResourceProvider를 만드는 데 사용되는CreateProvider 추상적인 방법이 있습니다.
   
   
   
   
1 : public abstract class ResourceProviderConfigurationElement: NameTypeConfigurationElement
2 : {
3 : public abstract IResourceProvider CreateProvider();
4 : }

DbResourceProvider와 XmlResourceProvider는 각각의 ResourceProviderConfigurationElement을 가지고 있는데 각각 DbResourceProviderConfigurationElement과 XmlResourceProviderConfigurationElement이다.
   
   
   
   
1 : public class DbResourceProviderConfigurationElement : ResourceProviderConfigurationElement
2 : {
3 : [ConfigurationProperty( " connectionStringName " , IsRequired = true )]
4 : public string ConnectionStringName
5 : {
6 : get { return ( string ) this [ " connectionStringName " ];}
7 : set { this [ " connectionStringName " ] = value;}
8 : }
9 : public override IResourceProvider CreateProvider()
10 : {
11 : return new DbResourceProvider( this .ConnectionStringName);
12 : }
13 : }
14 :
15 : public class XmlResourceProviderConfigurationElement : ResourceProviderConfigurationElement
16 : {
17 : [ConfigurationProperty( " fileName " , IsRequired = true )]
18 : public string FileName
19 : {
20 : get { return ( string ) this [ " fileName " ];}
21 : set { this [ " fileName " ] = value;}
22 : }
23 : public override IResourceProvider CreateProvider()
24 : {
25 : return new XmlResourceProvider( this .FileName);
26 : }
27 : }

세 가지 중요한 유형:NameTypeConfigurationElement과NameTypeConfigurationElementCollectionT
다음은 두 가지 중요한 유형을 소개하는데 첫 번째는 ResourceProviderConfigurationElement의 기본 클래스인 NameTypeConfigurationElement이다.말 그대로 NameType Configuration Element은 다음과 같은 두 가지 기본 구성 속성Name과 Type이 있는 구성 요소(Configuration Element)입니다.방법DeserializeElement는 비식별 설정 항목의 반서열화 문제를 해결하는 데 사용되는 것을 정의합니다.
   
   
   
   
1 : public class NameTypeConfigurationElement : ConfigurationElement
2 : {
3 : [ConfigurationProperty( " name " , IsRequired = true , IsKey = true )]
4 : public string Name
5 : {
6 : get { return ( string ) this [ " name " ];}
7 : set { this [ " name " ] = value;}
8 : }
9 : [ConfigurationProperty( " type " , IsRequired = true )]
10 : public string TypeName
11 : {
12 : get { return ( string ) this [ " type " ];}
13 : set { this [ " type " ] = value;}
14 : }
15 : public Type Type
16 : {
17 : get { return Type.GetType( this .TypeName);}
18 : }
19 : public void DeserializeElement(XmlReader reader)
20 : {
21 : base .DeserializeElement(reader, false );
22 : }
23 : }

또 다른 유형은 NameType Configuration Element의 설정 요소 집합(Configuration Element Collection): NameType Element CollectionT입니다.전체 배치 체계의 핵심이라고 할 수 있는데 그 전체 정의는 다음과 같다.
   
   
   
   
1 : public class NameTypeElementCollectionT : ConfigurationElementCollection where T : NameTypeConfigurationElement
2 : {
3 : protected override ConfigurationElement CreateNewElement()
4 : {
5 : return Activator.CreateInstanceT();
6 : }
7 : protected override object GetElementKey(ConfigurationElement element)
8 : {
9 : return (element as NameTypeConfigurationElement).Name;
10 : }
11 : protected virtual Type RetrieveConfigurationElementType(XmlReader reader)
12 : {
13 : Type configurationElementType = null ;
14 : if (reader.AttributeCount 0 )
15 : {
16 : for ( bool go = reader.MoveToFirstAttribute(); go; go = reader.MoveToNextAttribute())
17 : {
18 : if ( " type " .Equals(reader.Name))
19 : {
20 : Type providerType = Type.GetType(reader.Value, false );
21 : Attribute attribute = Attribute.GetCustomAttribute(providerType, typeof (ConfigurationElementTypeAttribute));
22 : if (attribute == null )
23 : {
24 : throw new ConfigurationErrorsException( " No ConfigurationElementTypeAttribute is applied. " );
25 : }
26 : configurationElementType = ((ConfigurationElementTypeAttribute)attribute).ConfigurationElementType;
27 : break ;
28 : }
29 : }
30 : reader.MoveToElement();
31 : }
32 : return configurationElementType;
33 : }
34 : protected override bool OnDeserializeUnrecognizedElement( string elementName, XmlReader reader)
35 : {
36 : if ( base .AddElementName.Equals(elementName))
37 : {
38 : Type configurationElementType = this .RetrieveConfigurationElementType(reader);
39 : var currentElement = (T)Activator.CreateInstance(configurationElementType);
40 : currentElement.DeserializeElement(reader);
41 : base .BaseAdd(currentElement, true );
42 : return true ;
43 : }
44 : return base .OnDeserializeUnrecognizedElement(elementName, reader);
45 : }
46 : public T GetConfigurationElement( string name)
47 : {
48 : return (T) this .BaseGet(name);
49 : }
50 : }

설정에 대해 우리는 다음과 같은 인식을 가져야 한다. 우리는 상응하는 유형을 통해 설정 파일의 어떤 XML 요소를 정의하는데, 읽을 때 실제로는 반서열화된 작업이다.한편, 성공적으로 서열화와 반서열화에 대해 그 근본적인 전제는 목표 유형을 확정하는 것이다. 왜냐하면 유형이 메타데이터를 묘사했기 때문이다.이 결론을 가지고 다시 XML로 표시된 설정과 ResourceSettings의 정의를 보면 다음과 같은 문제점을 발견할 수 있다. ResourceSetting의Providers 속성의 유형은 NameTypeElement Collection ResourceProvider Configuration Element이고 설정 요소 유형인 ResourceProvider Configuration Element은 추상적인 유형이다.구체적인 ResourceProvider 설정을 DbResourceProvider Configuration Element과 XmlResource Provider Configuration Element으로 반서열화해야 하는데 전체 설정 시스템에서 이 두 가지 유형의 그림자를 찾을 수 없을 것 같습니다.설정 요소가 실제 형식으로 반서열화되어야 한다는 것을 미리 정하지 못하면 전체 설정의 읽기가 실패합니다.구체적으로 말하면 DbProvider 요소의connectionStringName 속성과 XmlProvider의 fileName 속성을 식별할 수 없습니다. 기본 ResourceProviderConfigurationElement는 관련 속성의 정의가 없기 때문입니다.
기본적으로 구체적인 ResourceProvider의 설정 요소는 반서열화될 수 없고 식별할 수 없는 요소(Unrecognized Element)에 속하기 때문에 우리는 수동으로 반서열화를 실시하기만 하면 구체적인 방법은 Configuration Element Collection의 OnDeserialize Unrecognized Element 방법을 다시 쓰는 것이다.그러나 수동으로 반서열화를 하더라도 구체적인 설정 요소 유형을 정해야 하는데 어떻게 해결할 것인가?만약 당신이 충분히 자세하다면, DbResourceProvider와 XmlResourceProvider를 정의할 때, 클래스에 특수한 사용자 정의 기능을 적용합니다:ConfigurationElementTypeAttribute. 이것은 구체적인 ResourceProvider와 대응하는 설정 요소 간의 일치 관계를 구축합니다.
   
   
   
   
1 : [ConfigurationElementType( typeof (DbResourceProviderConfigurationElement))]
2 : public class DbResourceProvider : IResourceProvider
3 : {
4 : // ...
5 : }

이 Configuration ElementType Attribute 정의는 매우 간단합니다. 구성 요소의 유형을 나타내는 Configuration ElementType 속성만 정의하고 이 속성은 구조 함수에서 초기화됩니다.
   
   
   
   
1 : [AttributeUsage( AttributeTargets.Class)]
2 : public class ConfigurationElementTypeAttribute: Attribute
3 : {
4 : public Type ConfigurationElementType { get ; private set ; }
5 : public ConfigurationElementTypeAttribute(Type configurationElementType)
6 : {
7 : this .ConfigurationElementType = configurationElementType;
8 : }
9 : }

모든 구체적인 ResourceProvider는 이러한 Configuration ElementType Attribute를 가지고 대응하는 Configuration Element 유형을 지정하기 때문에 우리는 반서열화를 위해 설정 요소의 목표 유형을 정할 수 있습니다.이러한 작업은 RetrieveConfigurationElementType 메서드에서 수행됩니다.
4. ResourceProviderFactory의 정의
NameType Element CollectionT는 OnDeserialize Unrecognized Element 방법을 다시 쓰고 Configuration Element Type Attribute 특성을 빌려 식별할 수 없는 요소에 대한 해석 문제를 해결했다.구체적인 Resource Provider Configuration Element은 모두Create Provider 방법으로 대응하는 Resource Provider를 만들었다. 그러면 Resource Provider Factory의 실현은 매우 간단하다.
   
   
   
   
1 : public static class ResourceProviderFactory
2 : {
3 : public static IResourceProvider GetResourceProvider()
4 : {
5 : ResourceSettings settings = ResourceSettings.GetConfiguration();
6 : return GetResourceProvider(settings.DefaultProvider);
7 : }
8 : public static IResourceProvider GetResourceProvider( string name)
9 : {
10 : ResourceSettings settings = ResourceSettings.GetConfiguration();
11 : return settings.Providers.GetConfigurationElement(name).CreateProvider();
12 : }
13 : }

다섯
제 블로그를 자주 팔로우하시는 분들은 제가 마이크로소프트 오픈 프레임워크 EnterLib에 대해 어느 정도 알고 계신 걸 아실 거예요.EnterLib에 익숙한 친구들은 자주 번거로운 설정에 대해 비난을 하는데, 이것은 확실히 문제이다.그러나 이것은 또 다른 측면에서 EnterLib 베이스 설정 시스템의 강대함을 설명한다. 그렇지 않으면 이렇게 복잡한 설정을 지원하기 어렵다.EnterLib 구성 체계를 통해 사용자 정의 구성을 학습하는 것이 좋습니다.실제로 이 기사에서 식별할 수 없는 설정 요소에 대한 해결 방안은 EnterLib에서 나온 것이다.

좋은 웹페이지 즐겨찾기