在Groovy DSL中强制方法调用顺序

时间:2015-08-28 19:34:44

标签: groovy dsl

我正在使用Groovy开发一个小型DSL,我想知道是否有任何方法可以在方法调用中强制执行订单。

例如,这是有效的

SensorDSL.camera {
    take "picture" store_in "path" on {
        success "mySuccessCallback"
        cancel "myCancelCallback"
        error "myErrorCallback"
    }
}

但不应允许在store_in方法之前编写take

这是我目前的代码。

class SensorDSL {   
    def static camera(@DelegatesTo(CameraHandler) Closure closure){
        CameraHandler delegate = new CameraHandler()
        def code = closure.rehydrate(delegate, null, null)
        code.resolveStrategy = Closure.DELEGATE_ONLY
        code()
    }
}

class CameraHandler {
    String mediaType
    String path

    CameraCallbackHandler callbackHandler

    public CameraHandler(){
        callbackHandler = new CameraCallbackHandler()
    }

    CameraHandler take(String mediaType) {
        if (!MediaType.values().collect{it.toString()}.contains(mediaType.toUpperCase())){
            throw new Exception("Only PICTURE or VIDEO can be taken")
        }
        this.mediaType = mediaType
        this
    }

    CameraHandler store_in(String path){
        this.path = path
        this
    }

    void on(@DelegatesTo(CameraCallbackHandler) Closure closure){
        def code = closure.rehydrate(callbackHandler, null, null)
        code.resolveStrategy = Closure.DELEGATE_ONLY
        code.call()
    }

    class CameraCallbackHandler {
        String successCallback
        String errorCallback
        String cancelCallback

        CameraCallbackHandler success(String methodName){
            this.successCallback = methodName
            this
        }

        CameraCallbackHandler cancel(String methodName){
            this.cancelCallback = methodName
            this
        }

        CameraCallbackHandler error(String methodName){
            this.errorCallback = methodName
            this
        }
    }
}

如果有任何方法可以在没有人工检查的情况下进行方法调用,那也很棒。

编辑:我找到了一种似乎有用的方法。如果方法的返回是闭包映射,则可以按给定顺序调用方法。例如:

def take(String mediaType){
    [store_in: {path->
        this.path = path
        [on: { Closure closure->
            def code = closure.rehydrate(callbackHandler, null, null)
            code.resolveStrategy = Closure.DELEGATE_ONLY
            code.call()
        }]
    }]
}

但是这引起了IDE中没有给出代码完成的问题(我正在使用IntelliJ)。有没有其他方法可以强制命令,但是维护IDE的代码完成?

3 个答案:

答案 0 :(得分:2)

您可以为每个步骤创建自定义类,而不是Closing Map。例如, take()方法可以返回仅包含方法 store_in()的对象,该方法将返回一个对象,其中唯一的方法是 on() ,等等。

答案 1 :(得分:2)

两个简单的选择是:(a)维护一个强制执行呼叫顺序的状态机,例如,你不能从store_in移动到,或者(b)分解一些类。

状态机

可以通过多种方式实现状态机本身及其上下文的实施。简而言之,在您的安装人员中,您(a)检查当前状态,(b)确定您输入的状态是否为有效转换,以及(c)设置当前状态(&#) 34; next")表明是否允许转换。

在你的例子中,简言之:

CameraHandler take(String mediaType) {
    // State enforcement elided...

    this.state     = TAKEN
    this.mediaType = mediaType
    this
}

CameraHandler store_in(String path) {
  if (this.state != TAKEN) {
    throw new IllegalStateException("Media type must be specified")
  }

  this.state = STORED
  this.path  = path
  this
}

同样,有很多方法可以实现和强制执行状态机,例如,您可以拥有状态映射和可能的后续状态,以及状态处理程序。这可以是一个非常强大的工具。

打破功能

这就是伊曼纽尔的建议。不是返回CameraHandler(例如,当前实例),而是返回允许的任何类别" next"在命令链中。

在你的例子中:

take "picture" store_in "path" on { ... }

take方法将使用StorageHandler方法返回store_inStorageHandler将返回实现块内功能的任何内容。这有一些优点和缺点;您需要携带足够的状态才能使其正常工作 - 但您需要跟踪该状态某处以强制执行您的语义。将它分解为类可以使它们紧凑且易于测试,而神类中的状态机可以将注意力从基础功能转移出去。

固有的内部DSL(iDSL)限制

内部DSL(iDSL)非常酷;我很享受它们。但是,它们有一些限制,并且通常情况下,最小外部DSL(eDSL)允许更大的灵活性和实现选择。

例如,您提到了自动填充功能:动态语言已经很难自动完成,虽然将其分解为类可以帮助完成。它是否 取决于几个因素,例如语言的语法规则是否提供明确的类型解析。 (我最近没在Groovy工作过,所以我不确定。)

最小的eDSL可以在IDE中实现,例如IntelliJ的MPS,并为您提供完成。有基于Web的文本编辑器可以帮助突出显示和完成eDSL。一些eDSL和iDSL也可以使用相同的语法。

最终取决于您的需求。

答案 2 :(得分:0)

我找到了一种似乎有效的方法。如果方法的返回是闭包映射,则可以按给定顺序调用方法。例如:

def take(String mediaType){
    [store_in: {path->
        this.path = path
        [on: { Closure closure->
            def code = closure.rehydrate(callbackHandler, null, null)
            code.resolveStrategy = Closure.DELEGATE_ONLY
            code.call()
        }]
    }]
}

但是这引起了IDE中没有给出代码完成的问题......