代码之家  ›  专栏  ›  技术社区  ›  Roman Pokrovskij Archil Labadze

在调用链中维护顺序的“fluent接口”

oop
  •  2
  • Roman Pokrovskij Archil Labadze  · 技术社区  · 15 年前

    是否有一种优雅/方便的方法(不创建许多“空”类,或者至少它们不应该让人讨厌)让流畅的intercae在编译级别上保持有序。

    流畅的界面: http://en.wikipedia.org/wiki/Fluent_interface

    有一个想法允许这个汇编

    var fluentConfig = new ConfigurationFluent().SetColor("blue")
                                               .SetHeight(1)
                                               .SetLength(2)
                                               .SetDepth(3);
    

    拒绝这个

    var fluentConfig = new ConfigurationFluent().SetLength(2)
                                               .SetColor("blue")
                                               .SetHeight(1)
                                               .SetDepth(3);
    
    5 回复  |  直到 14 年前
        1
  •  1
  •   Michael Anderson    14 年前

    在C++中,我有三种方法使用编译时FSM来验证动作。你可以找到 code on github .

        2
  •  8
  •   Joel Mueller    15 年前

    链中的每个步骤都需要返回一个接口或类,该接口或类只包含在当前步骤之后有效使用的方法。换句话说,如果setcolor必须在第一位, ConfigurationFluent 应该只有setcolor方法。然后setcolor将返回一个只有setheight方法的对象,依此类推。

    实际上,返回值可能都是 配置Fluent 但强制转换到该类显式实现的不同接口。

        3
  •  1
  •   Berin Loritsch    15 年前

    简短的答案是否定的,没有优雅或方便的方法来强制构建一个类的顺序,当您链接时,它会正确地推动“流畅的接口”。

    答案越长,就要扮演魔鬼的拥护者。如果我有依赖属性(即需要先设置其他属性的属性),那么我可以实现它们,如下所示:

    method SetLength(int millimeters)
        if color is null throw new ValidationException
    
        length = millimeters
        return this
    end
    

    (注:以上不映射到任何真实语言,只是伪代码)

    所以现在我有一些例外需要担心。如果我不遵守规则,流畅的对象会抛出异常。现在假设我有一个像你一样的声明:

    var config = new Fluent().SetLength(2).SetHeight(1).SetDepth(3).SetColor("blue");
    

    当我抓住 ValidationException 因为长度取决于首先设置的颜色,我作为用户应该如何知道正确的顺序?即使我每个都有 SetX 方法在另一行上,stacktrace只会给出 config 变量是在大多数语言中声明的。此外,与其他物体相比,我该如何保持这个物体的规则在我的头脑中是笔直的呢?它是各种相互冲突的理想的混合体。

    这种优先级检查违背了“流畅接口”方法的精神。这种方法是为方便地配置复杂对象而设计的。当你试图强制执行命令时,你就失去了便利。

    为了正确、优雅地实现Fluent接口,有几个指导原则可以让您的类的消费者得到最好的遵守,谢谢:

    • 提供有意义的默认值:最小化更改值的需要,最小化创建无效对象的机会。
    • 除非明确要求,否则不要执行配置验证。当我们使用配置创建新的完全配置的对象时,或者当使用者显式调用 Validate() 方法。
    • 在抛出的任何异常中,请确保错误消息是清晰的,并指出任何不一致之处。
        4
  •  1
  •   ejaenv    14 年前

    也许编译器可以检查方法的调用顺序是否与定义的顺序相同。

    这可能是编译器的一个新功能。

    或者通过注释,比如:

    class ConfigurationFluent {
    
     @Called-before SetHeight
     SetColor(..)  {}
    
     @Called-After SetColor
     SetHeight(..) {}
    
     @Called-After SetHeight
     SetLength(..){ }
    
     @Called-After SetLength             
     SetDepth(..) {}
    
    }
    
        5
  •  0
  •   fabmilo    15 年前

    您可以实现有效操作序列的状态机,并且在每个方法上调用状态机,并验证是否允许操作序列,或者如果不允许,则引发异常。 不过,对于配置,我不建议使用这种方法,因为它会变得非常混乱,而且不可读。