我正在使用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的代码完成?
答案 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_in
。 StorageHandler
将返回实现块内功能的任何内容。这有一些优点和缺点;您需要携带足够的状态才能使其正常工作 - 但您需要跟踪该状态某处以强制执行您的语义。将它分解为类可以使它们紧凑且易于测试,而神类中的状态机可以将注意力从基础功能转移出去。
固有的内部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中没有给出代码完成的问题......