【问题标题】:How to refactor duplication out of closures that are an argument to a method call in Groovy?如何重构作为 Groovy 中方法调用参数的闭包中的重复?
【发布时间】:2014-09-07 16:21:58
【问题描述】:

考虑这个现实世界的例子,它是通过 Rest Client Builder(Grails 插件)对同一个 REST 服务端点进行“GET”和“POST”调用而编写的代码。我不喜欢重复,因为标题和内容类型设置是相同的,但我不确定如何重构公共部分,因为它们正在调用传递给 get() 或 post() 方法调用的闭包上的方法.请在您的答案中提供一个很好地重构重复的具体示例。

   private def doGetCall(String endpoint, def config) {
       def response = new RestBuilder().get(config.baseURI+endpoint) {
           contentType("application/json")
           header("Authorization", "ApiKey " + config.base64EncodedApiKey)
           header("ClientId", config.clientId)
       }

       handleResponse(response, config, endpoint);
       return response;
   }

   private def doPostCall(String endpoint, def payload, def config) {
       def response = new RestBuilder().post(config.baseURI+endpoint) {
           contentType("application/json")
           header("Authorization", "ApiKey " + config.base64EncodedApiKey)
           header("ClientId", config.clientId)
           json(payload)
       }

       handleResponse(response, config, endpoint, payload)
       return response;
   }

【问题讨论】:

    标签: grails groovy closures


    【解决方案1】:

    这就够了吗?

    class RestTestService {
        def rest
    
        def methodMissing(String name, args) {
            if( !( name in ['get', 'post'] ) ) { // can add PUT & DELETE in future
                // throw missing method exception for method names other than above
                throw new MissingMethodException(
                   "Http Method $name does not exist or not yet implemented.") 
            }
    
            def (endpoint, config, payload) = args?.toList()
    
            def response = rest."$name"(config.baseURI + endpoint) {
                contentType( "application/json" )
                header("Authorization", "ApiKey " + config.base64EncodedApiKey )
                header( "ClientId", config.clientId )
                if ( name == 'post' && payload ) {
                    json( payload )
                }
            }
    
            handleResponse(response, config, endpoint)
    
            return response
        }
    
        private void handleResponse(def response, def config, def endpoint) { ... }
    
        public def doGetCall(String endpoint, def config) {
            get( endpoint, config )
        }
    
        public def doPostCall(String endpoint, def payload, def config) {
            post( endpoint, config, payload )
        }
    }
    
    //resources.groovy
    beans = {
        rest(grails.plugins.rest.client.RestBuilder)
    }
    

    上面使用methodMissing 来决定在运行时调用哪个http方法。
    另请注意,我建议不要为每个http调用创建RestBuilder,而是将其用作上面resources.groovy中所示的bean,并在使用时将其注入到类中。如果它是一个 grails 工件(控制器、服务),那么它将自动连接,否则 bean rest 必须正确连接。 您可以使用doGetCalldoPostCall 进行抽象,或者在需要时完全删除它们。

    【讨论】:

    • 谢谢,聪明的解决方案,虽然我更喜欢@schmolli 的答案,因为我发现它一目了然更具可读性,而不必弄清楚为什么要使用 methodMissing。
    • 很公平,但您仍然需要在该解决方案的两种方法中重复大量代码。无论如何,get 和 post 方法中的这三行都是重复的。回到第一方,只有更少的代码行。您仍然有重复的代码行。这是methodMissing works的方法。我仍然同意闭包强制,它们很有效,但是 Groovy 中的 methodMissing 之类的功能使它更时髦和更干燥。 :) 一切顺利。
    【解决方案2】:

    Groovy 1.8 添加了 Closure 组合,因此如果您使用的是使用 Groovy 1.8 或更高版本的 Grails 版本:

    private def doGetCall(String endpoint, def config) {
        def response = new RestBuilder().get(config.baseURI+endpoint, composeRequest(config))
    
        handleResponse(response, config, endpoint);
        return response;
    }
    
    private def doPostCall(String endpoint, def payload, def config) {
        def response = new RestBuilder().post(config.baseURI+endpoint, composeRequest(config, { json(payload) }))
    
        handleResponse(response, config, endpoint, payload)
        return response;
    }
    
    private def composeRequest(def config, Closure clos = null) {
        def request = {
            contentType("application/json")
            header("Authorization", "ApiKey " + config.base64EncodedApiKey)
            header("ClientId", config.clientId)
        }
        if (clos != null) {
            request = request << clos
        }
        request
    }
    

    【讨论】:

    • 好答案。工作得很好,除了我不得不用 'request = request
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 2013-07-14
    • 2015-02-13
    • 2013-11-16
    相关资源
    最近更新 更多