Groovy DSL 中的强制方法调用顺序
Force method call order in Groovy DSL
我正在 Groovy 中开发一个小型 DSL,我想知道是否有任何方法可以强制方法调用中的顺序。
例如,这将是有效的
SensorDSL.camera {
take "picture" store_in "path" on {
success "mySuccessCallback"
cancel "myCancelCallback"
error "myErrorCallback"
}
}
但是在 take
方法之前写 store_in
是不允许的。
这是我当前的代码。
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
}
}
}
此外,如果有任何方法可以在无需手动检查的情况下进行所需的方法调用,那就太好了。
编辑: 我找到了一种似乎可行的方法。如果方法的 return 是闭包映射,则可以按给定顺序调用方法。例如:
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?
的代码完成之外,还有其他方法可以强制执行命令吗?
您可以为每个步骤创建自定义 类,而不是闭包映射。例如,take() 方法可以 return 一个仅包含方法 store_in() 的对象,这将return 一个对象,其唯一方法是 on(),依此类推。
我找到了一种似乎有效的方法。如果方法的 return 是闭包映射,则可以按给定顺序调用方法。例如:
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...
中没有给出代码完成的问题
两个简单的选择是 (a) 维护一个强制调用顺序的状态机,例如,您不能从 store_in 移动到 take,或者 (b) 分解一些 classes.
状态机
可以通过多种方式实现状态机本身并强制执行其上下文。简而言之,简单化是在您的设置器中,您将 (a) 检查当前状态,(b) 确定您进入的状态是否是有效转换,以及 (c) 设置当前 ("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
}
同样,有很多方法可以实现和强制执行状态机,例如,您可以拥有状态图和可能的后续状态,以及状态处理程序。这可能是一个非常强大的工具。
分解功能
这就是 Emmanuel 的建议。不是 returning CameraHandler
(例如,当前实例),而是 return 命令链中允许 "next" 的任何 class。
在你的例子中:
take "picture" store_in "path" on { ... }
take
方法将 return 一个 StorageHandler
和 store_in
方法。 StorageHandler
将 return 实现块内的功能。这有一些优点和缺点;你需要携带足够的状态来让它工作——但你需要在某处跟踪那个状态以加强你的语义。将其分解为 classes 可以使它们紧密且易于测试,而神 class 中的状态机可以将注意力从底层功能上转移开。
固有的内部 DSL (iDSL) 限制
内部 DSL (iDSL) 非常酷;我很喜欢它们。但是,它们有一些局限性,而且通常情况下,最小的外部 DSL (eDSL) 允许更大的灵活性和实现选择。
例如,您提到了自动完成:动态语言已经很难自动完成,尽管将内容分解为 class 可以对此有所帮助。它是否 会 取决于几个因素,例如语言的语法规则是否提供明确的类型解析。 (我最近在 Groovy 工作不多,所以我不确定。)
最小的 eDSL 可以在 IDE 中实现,例如 IntelliJ 的 MPS,并给你完成。有一些基于 Web 的文本编辑器可以帮助 eDSL 突出显示和完成。一些 eDSL 和 iDSL 也可以使用相同的语法。
最终还是要看你的需求了。
我正在 Groovy 中开发一个小型 DSL,我想知道是否有任何方法可以强制方法调用中的顺序。
例如,这将是有效的
SensorDSL.camera {
take "picture" store_in "path" on {
success "mySuccessCallback"
cancel "myCancelCallback"
error "myErrorCallback"
}
}
但是在 take
方法之前写 store_in
是不允许的。
这是我当前的代码。
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
}
}
}
此外,如果有任何方法可以在无需手动检查的情况下进行所需的方法调用,那就太好了。
编辑: 我找到了一种似乎可行的方法。如果方法的 return 是闭包映射,则可以按给定顺序调用方法。例如:
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?
的代码完成之外,还有其他方法可以强制执行命令吗?您可以为每个步骤创建自定义 类,而不是闭包映射。例如,take() 方法可以 return 一个仅包含方法 store_in() 的对象,这将return 一个对象,其唯一方法是 on(),依此类推。
我找到了一种似乎有效的方法。如果方法的 return 是闭包映射,则可以按给定顺序调用方法。例如:
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...
中没有给出代码完成的问题两个简单的选择是 (a) 维护一个强制调用顺序的状态机,例如,您不能从 store_in 移动到 take,或者 (b) 分解一些 classes.
状态机
可以通过多种方式实现状态机本身并强制执行其上下文。简而言之,简单化是在您的设置器中,您将 (a) 检查当前状态,(b) 确定您进入的状态是否是有效转换,以及 (c) 设置当前 ("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
}
同样,有很多方法可以实现和强制执行状态机,例如,您可以拥有状态图和可能的后续状态,以及状态处理程序。这可能是一个非常强大的工具。
分解功能
这就是 Emmanuel 的建议。不是 returning CameraHandler
(例如,当前实例),而是 return 命令链中允许 "next" 的任何 class。
在你的例子中:
take "picture" store_in "path" on { ... }
take
方法将 return 一个 StorageHandler
和 store_in
方法。 StorageHandler
将 return 实现块内的功能。这有一些优点和缺点;你需要携带足够的状态来让它工作——但你需要在某处跟踪那个状态以加强你的语义。将其分解为 classes 可以使它们紧密且易于测试,而神 class 中的状态机可以将注意力从底层功能上转移开。
固有的内部 DSL (iDSL) 限制
内部 DSL (iDSL) 非常酷;我很喜欢它们。但是,它们有一些局限性,而且通常情况下,最小的外部 DSL (eDSL) 允许更大的灵活性和实现选择。
例如,您提到了自动完成:动态语言已经很难自动完成,尽管将内容分解为 class 可以对此有所帮助。它是否 会 取决于几个因素,例如语言的语法规则是否提供明确的类型解析。 (我最近在 Groovy 工作不多,所以我不确定。)
最小的 eDSL 可以在 IDE 中实现,例如 IntelliJ 的 MPS,并给你完成。有一些基于 Web 的文本编辑器可以帮助 eDSL 突出显示和完成。一些 eDSL 和 iDSL 也可以使用相同的语法。
最终还是要看你的需求了。