【问题标题】:groovy generic fluent buildergroovy 通用流利构建器
【发布时间】:2012-03-23 08:52:01
【问题描述】:

我想创建一个简单的包装器,它允许将对象方法作为流畅的接口调用。我一直在考虑在创建时重写类的方法,但这似乎不起作用。这是否可以通过 groovy 元编程以某种方式实现?

到目前为止我有这种代码sn-p:

class FluentWrapper {

    def delegate

    FluentWrapper(wrapped) {
        delegate = wrapped

        delegate.class.getMethods().each { method ->
            def name = method.getName()
            FluentWrapper.metaClass."$name" = { Object[] varArgs ->
                method.invoke(wrapped, name, varArgs)
                return this
            }
        }
    }

    def methodMissing(String name, args) {
        def method = delegate.getClass().getDeclaredMethods().find { it.match(name) }
        if(method) {

            method.invoke(delegate,name, args)
            return FluentWrapper(delegate)
        }
        else throw new MissingMethodException(name, delegate, args)
    }

}

假设示例 Java 类:

class Person {
    void setAge()
    void setName()
}

我希望能够执行以下代码:

def wrappedPerson = new FluentWrapper(new Person())
wrappedPerson.setAge().setName()

我为此使用了 Groovy 1.6.7。

【问题讨论】:

    标签: groovy metaprogramming dsl


    【解决方案1】:

    这都是 Groovy,我使用的是 1.8.6(当前最新版本),但是给定了这个 Person 类:

    class Person {
      int age
      String name
    
      public void setAge( int age ) { this.age = age }
      public void setName( String name ) { this.name = name }
      public String toString() { "$name $age" }
    }
    

    还有这个 FluentWrapper 类:

    class FluentWrapper {
    
      def delegate
    
      FluentWrapper(wrapped) {
        delegate = wrapped
      }
    
      def methodMissing(String name, args) {
        def method = delegate.getClass().declaredMethods.find { it.name == name }
        if(method) {
          method.invoke( delegate, args )
          return this
        }
        else throw new MissingMethodException(name, delegate, args)
      }
    }
    

    那么,你应该可以做到:

    def wrappedPerson = new FluentWrapper(new Person())
    
    Person person = wrappedPerson.setAge( 85 ).setName( 'tim' ).delegate
    

    person 应该指定年龄和姓名

    【讨论】:

    • 太好了,所以使用新版本正是我应该做的:) 我会在星期一测试这个。
    【解决方案2】:

    我觉得 @tim_yates 的 answer 很好,但是您无法访问 delegate 方法的返回值(这是人们通常喜欢做的事情,即使对于 build() 的 Builder 来说也是如此:)

    此外,如果这不是针对 Builder 而是针对具有可链接接口的对象(例如 JS 中的 jQuery 包装对象),那将是一个严重的问题。

    所以我会像这样放置包装器:

    class FluentWrapper {
    
      def delegate
    
      FluentWrapper(wrapped) {
        delegate = wrapped
      }
    
      def methodMissing(String name, args) {
        def method = delegate.getClass().declaredMethods.find { it.name == name }
        if(method) {
          def result = method.invoke(delegate, args)
          return result != null ? result : this
        }
        else throw new MissingMethodException(name, delegate, args)
      }
    }
    

    请注意,elvis 运算符不合适,因为永远不会返回虚假值。

    当然,方法是否可链接取决于调用者,但如果需要,可以通过方法注释来克服。

    【讨论】:

    • 这实际上是在经典流利构建器方法的情况下更好的解决方案。但是我不会这样做,因为我想执行多个函数而不必过多担心包装对象的内容。也许合并这些方法并添加一些构造函数值会很好,以便决定哪些行为适用于这个特定的包装器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-15
    • 2016-05-12
    • 2019-12-18
    • 2015-08-29
    • 1970-01-01
    • 2010-12-13
    相关资源
    最近更新 更多