体验流畅的界面?我需要你的意见!

ang*_*son 3 extension-methods extensibility fluent-nhibernate

很抱歉这个很长的问题,它被标记为维基,因为我要求的东西可能没有一个非常具体的答案.如果它关闭了,那就这样吧.

我的主要问题是:

你如何编写一个未在基类中完全定义的流畅接口,以便使用流畅接口的程序可以在现有结构中添加新单词,并且仍然保持一个引导接口,以便在点之后,智能感知仅列出此时实际应用的关键字.


我正在重写我的IoC容器的第三次迭代.第二次迭代是为了提高性能,第三次迭代将解决一些可扩展性问题和分离问题.

基本上,可扩展性的问题是没有.我最近想要使用具有生命周期的服务,并在生命周期结束后,解决一个新的副本.例如,每分钟读取一个配置文件,但不是更频繁.我目前的IoC解决方案不支持这种方法,但添加它的唯一方法是进入基类库并在那里添加对它的支持.这对我来说意味着我无法构建可扩展的类库.平心而论,我并不打算在其中构建可扩展性,但后来我并没有充分意识到进入并稍后添加类似内容会带来多大的痛苦.

我正在寻找我的流畅的配置界面,因为我想在界面中构建完全可扩展性(或者摆脱它,我不愿意这样做)我需要以不同的方式做事.

因此,我需要你的意见.我实际上使用流畅的接口的经验很少,但我已经看到了很多使用它们的代码,因此开箱即用有一个明显的好处:

  • 使用流畅接口的代码通常很容易阅读

换句话说,这个:

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

比这更容易阅读:

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

所以可读性是一个问题.

但是,程序员指导是另一种,通过在网络上或在编辑器中阅读现有代码而不易理解的东西.

基本上,当我输入这个:

ServiceContainer.Register<ISomeService>()
    .From.|
          ^-cursor here
Run Code Online (Sandbox Code Playgroud)

然后intellisense将显示可用的分辨率类型.在我挑选那个之后,写道:

ServiceContainer.Register<ISomeService>()
    .From.ConcreteType<SomeService>()
    .For.|
Run Code Online (Sandbox Code Playgroud)

然后我只能在"For"关键字之后获得可用的内容,例如"Policy"等.

但是,这是一个大问题吗?您使用过的流畅界面是这样的吗?明确定义接口的警告是使用所有关键字和所有内容创建一个类或接口,以便每个逗号后的intellisense包含所有内容,但这也可能导致这是合法的(如,它编译)代码:

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

所以我想构建流畅的接口,以便在您指定如何解析服务后,再也不能这样做了.

  • 换句话说,流畅的界面非常易于使用,因为它们可以引导您完成您的工作.

但这是典型的吗?因为我希望能够添加一堆这样的关键字,比如解析器的类型(ConcreteType,Delegate等),范围的类型(Factory,Container,Singleton,Cache等)作为扩展方法,这样程序可以定义自己的方法,而无需进入和更改基类,这意味着我需要为所有中间停止提供接口,并让实际的重要关键字.然后,这些关键字的实现必须选择一个中间停止接口以便返回.

所以看起来我需要为以下内容定义一个接口:

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

等等,但对我来说看起来很分散.

任何有流畅界面经验的人都可以回到顶部并阅读我的引用答案,并试着给我一个简短的答案吗?

Jam*_*ory 6

两件事:扩展方法和嵌套闭包.它们应该涵盖您的所有可扩展性和智能感知清晰度需求.


如果您有兴趣,请参阅我建立Fluent NHibernate的经验中的一些提示.

方法链应该保持在最低限度.除了其他因素之外,它导致了死胡同和无限期的呼叫链结束.喜欢嵌套的闭包.

例如,死胡同:

Database
  .ConnectionString
    .User("name")
    .Password("xxx")
  .Timeout(100) // not possible
Run Code Online (Sandbox Code Playgroud)

你不能回Database链一旦你进入了ConnectionString链,因为没有办法回了所有的连接字符串相关方法返回的一个实例ConnectionString.

你可以用一个明确的方法重写它,但它们很难看.

Database
  .ConnectionString
    .User("name")
    .Pass("xxx")
    .Done()
  .Timeout(100)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,Done将返回Database实例,返回主链.再次,丑陋.

如建议的那样,更喜欢嵌套的闭包.

Database
  .ConnectionString(cs =>
    cs.User("name");
      .Pass("xxx"))
  .Timeout(100);
Run Code Online (Sandbox Code Playgroud)

这几乎涵盖了你的intellisense问题,因为闭包是相当独立的.您的顶级对象将仅包含采用闭包的方法,而这些闭包仅包含特定于该操作的方法.可扩展性在这里也很容易,因为您可以仅在闭包内公开的类型中添加扩展方法.

您还应该注意不要尝试将您的流畅界面读成英语. UseThis.And.Do.That.With.This.BecauseOf.That当动词足够时,链只会使你的界面变得复杂.

Database
  .Using.Driver<DatabaseDriver>()
  .And.Using.Dialect<SQL>()
  .If.IsTrue(someBool)
Run Code Online (Sandbox Code Playgroud)

与:

Database
  .Driver<DatabaseDriver>()
  .Dialect<SQL>()
  .If(someBool)
Run Code Online (Sandbox Code Playgroud)

智能感知中的可发现性降低了,因为人们倾向于寻找动词而未能找到动词.FNH的一个例子是WithTableName方法,人们倾向于寻找单词而不是找到它,因为该方法.

您的界面也变得更难以用于非母语英语使用者.虽然大多数非母语人士会知道他们正在寻找的技术术语,但额外的话可能并不清楚.