How to refactor duplication out of closures that are an argument to a method call in Groovy?

社会主义新天地 提交于 2019-12-24 12:01:57

问题


Consider this real world example, which is code written to make 'GET' and 'POST' calls to the same REST service endpoint via the Rest Client Builder (Grails plugin). I dislike the duplication in that the header and content type setup is identical, but am not sure how to refactor the common pieces out given that they are calling methods on the closure that is being passed to the get() or post() method call. Please provide a concrete example of a good refactoring out of the duplication in your answer.

   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;
   }

回答1:


Groovy 1.8 added Closure composition, so if you're using a version of Grails which uses Groovy 1.8 or greater:

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
}



回答2:


Will this suffice?

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)
}

Above makes use of methodMissing in order to decide which http method to call during runtime.
Also note, instead of creating RestBuilder for each http call, I suggest to use it as a bean as shown above in resources.groovy and inject it to the class when it is used. If it were a grails artifact (controller, service) then it will be autowired otherwise bean rest has to be wired appropriately. You might abstract by using doGetCall and doPostCall or totally remove them if required.



来源:https://stackoverflow.com/questions/24792649/how-to-refactor-duplication-out-of-closures-that-are-an-argument-to-a-method-cal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!