Attention | Topic was automatically imported from the old Question2Answer platform. | |
Asked By | lvl7 |
First of all, I’m quite new in GDscript and godot itself so even unrelated hints are welcome.
I’m trying to implement specification pattern in GDScript. Simplified, not-working implementation:
SpecificationInterface.gd
:
extends Resource
class_name SpecificationInterface
# abstract
func is_satisfied(candidate) -> bool:
push_error("need to be implemented")
return false
BaseSpecification.gd
:
extends SpecificationInterface
class_name BaseSpecification
class AndSpecification:
extends BaseSpecification # <-- cyclic dependency
var first: SpecificationInterface
var second: SpecificationInterface
func is_satisfied(candidate) -> bool:
return self.first.is_satisfied(candidate) and self.second.is_satisfied(candidate)
func and_(other: SpecificationInterface):
var and_specification := AndSpecification.new()
and_specification.first = self
and_specification.second = other
return and_specification
The problem is: AndSpecification
can’t extend BaseSpecification
from script where it’s defined. It can’t be defined in separated script either because BaseSpecification
use AndSpecification
.
I tired some dirty hack where BaseSpecification
call FuncRef
of AndSpecification.is_satisfied
but it’s also don’t work:
extends SpecificationInterface
class_name BaseSpecification
var _validation_method: FuncRef # (candidate) -> bool
func is_safisfied(candidate) -> bool:
if self._validation_method != null:
return self._validation_method.call_func(candidate)
push_error("need to be implemented")
return false
class AndSpecification:
var first: SpecificationInterface
var second: SpecificationInterface
func is_satisfied(candidate) -> bool:
return self.first.is_satisfied(candidate) and self.second.is_satisfied(candidate)
func and_(other: SpecificationInterface): # -> LogicSpecificationInterface:
var and_specification := AndSpecification.new()
and_specification.first = self
and_specification.second = other
# using own name is not allowed - cyclic dependency again
# var new_sepc: = BaseSpecification.new()
# this, surprisingly, is allowed, however it duplicates
# my extension of `BaseSpecification` and not pure
# `BaseSpecification` itself. So `is_satisfied` is already overwritten
# and my dirty hack doesn't work.
var new_spec: = (self as BaseSpecification).duplicate()
new_spec._validation_method = funcref(new_spec, "is_satisfied")
return and_specification
This is a very interesting design pattern I’ve never heard of until now. Can I ask what some high-level goals you have for the pattern are?
Also, this isn’t really an answer, but godot supports mingling of GDScript, C#, and GDNative scripts among others. It might not be ideal, but have you considered lifting the C# example from the Wiki page you linked for your implementation? I haven’t tried using C# in my projects so I’m not sure if there’d still be odd linking issues or not.
DownloadFLD | 2022-07-26 22:20
I’m working on a game where a player can build some machine from parts. There’re couple kinds of parts like engine, manipulator, life-support. Each kind has it’s own general rules, for example a manipulator can’t be placed directly near an engine. Besides that, each particular part can have special rules, like require at least 5 energy or be connected with life-support.
On top of that, I decided that parts (and their rules) are defined as json file and loaded during gameplay. It’d make game extension/modification trivial, even for non-developers.
I can define atomic rules in code and mention them with logic operators, in json:
{
"rule": {
"or": [
{"and": [{"connected_with": "ENGINE"}, {"min_energy": 5}]},
{"connected_with": "LIFE_SUPPORT"}
]
}
}
Just writing this example, it made me thinking that I… probably don’t need implementation of specification pattern after all… I’ve just wrote them with normal Polish notation so it should be feasible to create combined rules in the code.
With specification pattern I could write in code:
rule = (
ConnectedWith("ENGINE").and(MinEngery(5))
).or(Connected_with("LIFE_SUPPORT")
Which is more readable but I have no idea how wanted to parse it to json…
Oh my! Thank you! I should mention my goals in initial post to avoid xy problem.
Also, for some reason, it didn’t even cross my mind that I can use multiple languages at the same time (thanks for that also!). As you said, it isn’t answer I was looking for, however it should work. With small drawback: cross language inheritance is forbidden.
lvl7 | 2022-07-28 18:49
Ah nice, glad you figured it out! I’ve had lots of moments like that where, in the middle of asking for help or describing my problem again, a solution will strike me. Sounds like you know exactly what you need and how to get it.
I was not aware that cross-language inheritance was forbidden, that would definitely throw a wrench into things here or there. Makes sense, though.
DownloadFLD | 2022-07-28 21:50