2009-12-27 2 views
3

이 긴 질문에 대해 유감스럽게 생각합니다. 아주 구체적인 답변이 없을 수도있는 것이 있기 때문에 위키에 플래그가 지정되어 있습니다. 그것이 닫혀 있다면, 그렇게하십시오. 유창 인터페이스를 사용하는 프로그램은 기존의 내부에 새로운 단어에 압정 할 수 있도록 완전히 기본 클래스에 정의되지 않은 유창 인터페이스를 작성합니다 어떻게유창한 인터페이스 경험이 있으십니까? 나는 너의 의견이 필요해!

을 :

내 주요 질문은 이것이다 구조를 유지하고지도 인터페이스를 유지하므로 도트 뒤에 인텔리 센스는이 시점에서 실제로 적용되는 키워드 만 나열합니다.


나는 내 IoC 컨테이너를 재 작성의 제 3 반복에있어. 두 번째 반복은 성능을 향상시키는 것이었고 세 번째 반복은 확장 성 문제와 분리 문제를 해결하는 것입니다.

기본적으로 확장 성 문제는 아무 것도 없다는 것입니다. 최근에 평생 서비스를 사용하고 싶었고 평생 서비스가 만료 된 후 새로운 사본을 해결합니다. 예를 들어 매분마다 구성 파일을 읽지 만 더 자주는 아닙니다. 이것은 현재의 IoC 솔루션에서 지원되지 않지만 기본 클래스 라이브러리에 들어가서 거기에 지원을 추가하는 것이 유일한 방법입니다. 즉, 확장 가능한 클래스 라이브러리를 작성하지 못했습니다. 모든 공정성에있어서, 나는 그것에 확장 성을 지 으려고하지 않았지만, 그런 다음에는 얼마나 고통 스러울지를 충분히 인식하지 못했고 나중에 이와 같은 것을 추가했습니다.

구성에 대한 유창한 인터페이스를보고 있으며, 인터페이스에 대한 완전한 확장 성을 구축하고 싶기 때문에 (또는 제거 할 필요가 있습니다.) 나는 다르게해야합니다.

마찬가지로, 귀하의 의견이 필요합니다. 유창한 인터페이스를 실제로 사용하는 경험은 거의 없지만 사용하는 코드가 상당히 많아서 즉시 사용할 수있는 이점이 하나 있습니다.

  • 유창한 인터페이스를 사용하는 코드는 다음과 같습니다.

    ServiceContainer.Register<ISomeService>() 
        .From.ConcreteType<SomeService>() 
        .For.Policy("DEBUG") 
        .With.Scope.Container() 
        .And.With.Parameters 
         .Add<String>("connectionString", "Provider=....") 
         .Add<Boolean>("optimizeSql", true); 
    

    이보다 쉽게 ​​읽을 수있다 : 즉

, 숙지 일반적으로 매우 쉽게

ServiceContainer.Register(typeof(ISomeService), typeof(SomeService), 
    "DEBUG", ServiceScope.Container, new Object[] { "Provider=...", true }); 

가독성은 하나의 문제입니다.

그러나 프로그래머 지침은 기존 코드를 웹이나 편집기를 통해 쉽게 이해할 수없는 또 다른 것입니다.

기본적으로,이를 입력 할 때 :

ServiceContainer.Register<ISomeService>() 
    .From.| 
      ^-cursor here 

와는 IntelliSense를 사용 가능한 해상도의 종류를 표시합니다. 그 중 하나를 고른 후 다음과 같이 작성하십시오 :

그런 다음 "정책"과 같은 "For"키워드 다음에 사용할 수있는 항목 만 제공됩니다.

그러나 이것은 큰 문제입니까? 유창한 인터페이스를 사용해 본 경험이 있습니까?인터페이스를 정의하는 확실한 방법은 클래스 또는 인터페이스를 모든 키워드와 모든 항목으로 만드는 것입니다. 각 쉼표 뒤에 모든 정보가 포함 된 IntelliSense가 포함될 수도 있지만 이것이 합법적 인 경우도 발생할 수 있습니다 (예 : 컴파일) 코드 :

ServiceContainer.Register<ISomeService>() 
    .From.ConcreteType<SomeService>() 
    .From.Delegate(() => new SomeService()) 
    .From.With.For.Policy("Test"); 

그래서 난 당신이 서비스를 해결하는 방법을 지정한 후, 다시 그렇게 할 수있는 유창한 인터페이스는 구조 싶습니다.

  • 즉, 유창한 인터페이스는 수행 할 수있는 방향으로 안내하기 때문에 매우 사용하기 쉽습니다.

하지만 이것은 일반적인 것입니까? Resolver (ConcreteType, Delegate 등) 유형, 범위 유형 (Factory, Container, Singleton, Cache 등)의 유형을 확장 메소드로 추가하여 이러한 키워드를 추가 할 수 있으므로 프로그램은 기본 클래스를 변경하지 않고도이를 수행 할 수있는 고유 한 방법을 정의 할 수 있습니다. 즉, 모든 중간 정지를위한 인터페이스를 제공하고 실제 중요한 키워드를 제공해야 함을 의미합니다. 그런 다음 해당 키워드를 구현하면 적절하게 반환 할 하나의 중간 중지 인터페이스를 선택해야합니다.

  • xyz.From : 내가위한 인터페이스를 정의 할 필요가 같은

    그래서 그것은 본다.

  • <Resolver here>.With.
  • <Resolver here>.For.

  • xyz.From.<Resolver here>.
  • 하지만 나에게 조각 보인다.

    유창한 인터페이스 경험이있는 사람이라면 누구나 돌아가서 가장 가까운 곳에서 인용 한 대답을 읽고 나에게 짧은 대답을 줄 수 있습니까?

    +1

    이럴 .With.Scope.Container() And.With.Parameters ...유창한 학대처럼 보입니다. –

    +0

    내가 말했듯이, 나는 많은 사용 경험이 없다. 어떻게 구조 조정을하겠습니까? –

    답변

    6

    두 가지 : 확장 메소드와 중첩 된 클로저. 그들은 귀하의 모든 확장 성 및 인텔리 센스 명확성 요구를 충족시켜야합니다. 당신이 관심이 있다면


    , 여기 Fluent NHibernate 건물 내 경험에서 몇 가지 팁입니다.

    메소드 체이닝은 최소한으로 유지해야합니다. 다른 말로는 콜 체인 (call-chain)에 막 다른 골목 (dead-ending)과 끝 (indefinite end)이 생깁니다. 중첩 폐쇄를 선호합니다. 예를 들어

    , 죽은 끝 :

    Database 
        .ConnectionString 
        .User("name") 
        .Password("xxx") 
        .Timeout(100) // not possible 
    

    당신은 다시 Database 체인을 얻을 수있는 방법은 모든 연결 문자열을 사용하여 백업을가 없기 때문에 당신의 ConnectionString 체인을 입력하면 관련 메서드는 ConnectionString의 인스턴스를 반환합니다.

    확실한 방법으로 다시 작성할 수는 있지만 추한 것입니다.

    Database 
        .ConnectionString 
        .User("name") 
        .Pass("xxx") 
        .Done() 
        .Timeout(100) 
    

    이 경우, Done는 주 사슬로를 반환 Database 인스턴스를 반환 곳.다시, 추한.

    제안 된대로 은 내포 된 클로저을 선호합니다.

    Database 
        .ConnectionString(cs => 
        cs.User("name"); 
         .Pass("xxx")) 
        .Timeout(100); 
    

    클로저는 상당히 자체적으로 포함되어 있기 때문에 인텔리 센스 문제를 거의 다루고 있습니다. 최상위 개체에는 클로저를 사용하는 메서드 만 포함되며 이러한 클로저에는 해당 작업과 관련된 메서드 만 포함됩니다. 클로저 내부에 노출 된 유형에만 확장 메서드를 추가 할 수 있기 때문에 확장 성도 매우 쉽습니다.

    또한 유창한 인터페이스를 영어와 같이 읽으려고 시도하지 말아야합니다. UseThis.And.Do.That.With.This.BecauseOf.That 체인은 동사가 충분할 때만 인터페이스를 복잡하게합니다.

    Database 
        .Using.Driver<DatabaseDriver>() 
        .And.Using.Dialect<SQL>() 
        .If.IsTrue(someBool) 
    

    는 수스 : 사람들이 동사에 대한보고를 찾기 위해 실패하는 경향이 있기 때문에 인텔리의

    Database 
        .Driver<DatabaseDriver>() 
        .Dialect<SQL>() 
        .If(someBool) 
    

    검색 기능은 감소한다. FNH의 예는 표 표으로 시작하여 으로 시작하여으로 시작하기 때문에 찾을 수없는 WithTableName 방법입니다.

    영어가 모국어가 아닌 사람에게도 인터페이스가 사용하기가 더 어려워집니다. 대부분의 외국어 모국어 사용자는 원하는 내용에 대한 전문 용어를 알 수 있지만 추가 단어가 명확하지 않을 수 있습니다.

    +0

    내 모든 문제가 해결되는 것 같습니다. "With"와 "For"및 "By"키워드에 대한 유일한 목적은 "registration.AnythingHere"를 피하기 위해 동사 목록을 제한하는 것이었지만 closure 구문은 매우 좋을 것 같습니다. 내 목적을 위해. –

    +0

    그것은 잠재적으로 또 다른 문제를 해결할 것입니다. 코드에서 "cs"인수를 사용하는 대리자를 사용하는 ConnectionString 메서드를 사용하여이 메서드는 인증 할 수있는 무언가를 전달하는 경우에만 유효하다고 가정합시다. "User"가 "Password"또는 "Pass"메서드를 사용하여 중간 개체를 반환하고 "Pass"메서드 만 올바른 인터페이스를 반환하도록하면 사용자가 완전한 인증을 유도하는 무언가를 사용해야합니다. 물론 "cs"개체는 해당 개체에 대한 다양한 경로를 가질 수 있지만 잘 보입니다. 또는 나는 다시 복잡하게하고 있는가? –

    +0

    나는 당신의 대답에 기초하여 생각해 낸 문법으로 답변을 게시 할 것입니다. 당신이 그것을 생각하고 당신이 생각하는 것에 대해 덧글을 남길 수도 있습니까? –

    0

    @James Gregory이 제공하는 answer을 기반으로 IoC 컨테이너를위한 새로운 프로토 타입 유창한 인터페이스를 만들었습니다.

    이 내 현재의 문제 해결

    1. 확장, 나는 유창 인터페이스를 작성하는 확장 메서드
    2. 의 쉬운 등 새로운 해상도의 종류, 새 범위 유형을 추가 할 수 있습니다를 필요는 리드 키워드를 중복 없습니다 동일한 경로 접미사로,
    3. 많은 적은 코드 제 1 및

    모든 코드가 내 샌드 박스에서 컴파일 두번째 반복 구현에 비해, 그래서 모든 법적 구문의 N 물론 그 방법은 현재 아무 것도하지 않는다는 것을 제외하고는, 거짓은 가짜입니다.

    내가 결정한 한 가지가 이 아니기 때문에은 인터페이스를 따라 이동할 때 선택 사항을 제한하는 유창한 인터페이스의 지침 부분입니다. 따라서,이 쓰기 완벽 유효입니다 :

    IoC.Register<ILogger>() 
        .From(f => f.ConcreteType<TestLogger>()) 
        .From(f => f.ConcreteType<AnotherLogger>()); // note, two From-clauses 
    

    은 아마도 나는이 예외 (이미 설정 해상도 객체) 또는 마지막 승리하는 경우가 발생합니다 경우 선택해야하는거야.

    의견을 남겨주세요. .

    여기에 코드입니다 :

    using System; 
    
    namespace IoC3rdIteration 
    { 
        public class Program 
        { 
         static void Main() 
         { 
          // Concrete type 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()); 
    
          // Concrete type with parameters 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<DatabaseLogger>(ct => ct 
            .Parameter<String>("connectionString", "Provider=...") 
            .Parameter<Boolean>("cacheSql", true))); 
    
          // Policy 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Policy("DEBUG"); 
    
          // Policy as default policy 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Policy("RELEASE", p => p.DefaultPolicy()); 
    
          // Delegate 
          IoC.Register<ILogger>() 
           .From(f => f.Delegate(() => new TestLogger())); 
    
          // Activator 
          IoC.Register<ILogger>() 
           .From(f => f.Activator("IoC3rdIteration.TestService")); 
    
          // Instance 
          IoC.Register<ILogger>() 
           .From(f => f.Instance(new TestLogger())); 
    
          // WCF-wrapper 
          IoC.Register<ILogger>() 
           .From(f => f.WCF()); 
    
          // Sinkhole service 
          IoC.Register<ILogger>() 
           .From(f => f.Sinkhole()); 
    
          // Factory 
          IoC.Register<IServiceFactory<ILogger>>() 
           .From(f => f.ConcreteType<LoggerFactory>()); 
          IoC.Register<ILogger>() 
           .From(f => f.Factory()); 
    
          // Chaining 
          IoC.Register<IDebugLogger>() 
           .From(f => f.ConcreteType<DatabaseLogger>()); 
          IoC.Register<ILogger>() 
           .From(f => f.ChainTo<IDebugLogger>()); 
           // now "inherits" concrete type 
    
          // Generic service 
          IoC.Register(typeof(IGenericService<>)) 
           .From(f => f.ConcreteType(typeof(GenericService<>))); 
    
          // Multicast 
          IoC.Register<ILogger>() 
           .From(f => f.Multicast(
            r1 => r1.From(f1 => f1.ConcreteType<TestLogger>()), 
            r2 => r2.From(f2 => f2.Delegate(() => new TestLogger())), 
            r3 => r3.From(f3 => f3.Instance(new DebugLogger())))); 
    
          // Factory-scope 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Scope(s => s.Factory()); 
    
          // Thread-scope 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Scope(s => s.Thread()); 
    
          // Session-scope (ASP.NET) 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Scope(s => s.Session()); 
    
          // Request-scope (ASP.NET) 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Scope(s => s.Request()); 
    
          // Singleton-scope 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Scope(s => s.Singleton()); 
    
          // Singleton-scope with lifetime 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Scope(s => s.Singleton(si => si.LifeTime(10000))); 
    
          // Container-scope 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Scope(s => s.Container()); 
    
          // Container-scope with lifetime 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Scope(s => s.Container(c => c.LifeTime(10000))); 
    
          // Pooled-scope 
          IoC.Register<ILogger>() 
           .From(f => f.ConcreteType<TestLogger>()) 
           .Scope(s => s.Pool(p => p 
            .Minimum(1)    // always one instance in pool 
            .Typical(5)    // reduce down to 5 if over 5 
            .Maximum(10)   // exception if >10 in pool 
            .AutoCleanup()   // remove on background thread >5 
            .Timeout(10000)));  // >5 timeout before removal 
         } 
        } 
    } 
    
    관련 문제