I’ve been reading “Effective Java” and listening to the Coding Blocks podcasts fairly regularly now, and they’ve proved an amazing resource. I’m finally thinking in terms of design patterns instead of spaghetti. With that, it has made using the debugger and rapidly developing plugin resources much faster.
That being said, I’m not quite sure the best way to adapt these practices to config files. I assume there are important relations to factory patterns here, but it does seem like hard coding in getNode(“Some Vaguely Human Readable Node”) is not the best way to go about it.
So for projects where you guys have a fairly large number of constants/user-changable constants, how do you integrate ConfigurationNode-based constants without your code turning into spaghetti?
You can create a class annotated with ConfigSerializable, and then declare fields with the @Setting annotation. (See here to read more about it) That way you can just have a Config class which holds all values you need as variables.
Like @Wundero I would move all configurable variables into a separated class and have this class handle loading and parsing of the configuration file (via ConfigSerializable or anything else). You can than inject this class into every class that depends on configurable values.
I would not, however, use public fields to store the configurable values. Unless static and final, public fields are (nearly) always a very bad idea, because you cannot control any external changes made to these variables - plus you cannot validate the state of a certain field before handling its value out to other classes. Instead, I would recommend using private fields together with getters to be used by other classes.
If you are using getters it is also a good idea to introduce an interface that actually defines all these getters and have a class implementing this interface that connects it with Sponge’s configuration logic. This way, classes that depend on your configuration can be programmed against the interface.
You can swap out the configuration implementation at any time and unit testing becomes much easier because you can easily mock the interface.
@pie_flavor
Can you adapt a @Configserializable/@Setting method of holding node values and use it to set default values? It seems like this would be a great way to generate a config file.
Another snag I ran into whilst trying your approach is that static classes in Config.java don’t allow for instantiation (obviously). My use case:
Region1:
-a private primitive value
-a private SubRegion value1
// what I’d like to do:
-a private SubRegion value1 = new SubRegion(specifics to Region1)
Region2:
-a private primitive value
-a private SubRegion SubRegion value2
// what I’d like to do:
-a private SubRegion value2 = new SubRegion(specific defaults to Region2)
SubRegion:
a constructor that sets the primitive default value?
-a primitive value
If anyone is willing to cobble together improved documentation for this and throw a PR our way, SpongeDocs editorial staff will definitely give it fair consideration. Clarity and ease-of-use are important features.
Trial by fire (aka actual live testing) is definitely spurring the evolution of Sponge and it’s Docs
Yes, if you set it like that 2 will be the default value. If you didn’t set it to anything, is will use the normal default value for that type (0 for int, false for boolean, 0D for double and so on). Watch out for what the default value is, as it’s a nice player for bugs to find their way in.
As for how to write the config, you’ll have to do that yourself.
I guess I’m a bit confused as the docs say “Since in many cases the (de)serialization boils down to mapping fields to configuration nodes, writing such a TypeSerializer is a rather dull affair and something we’d like Configurate to do on its own.”
I guess whomever wrote this meant “deserialization” without the (de) parenthesis, which would mean if you wanted to serialize your object you would need to provide custom TypeSerializers. Mk I can do that, I was just wondering if there wasn’t a shortcut as was lightly implied by the docs.
I think you should be able to save defaults. You can load a root node from a file and then copy it; use root.setValue(TypeToken.of(Config.class), config); on one of the roots, then merge values such that the root that you did NOT set the value of takes priority. Then you can save the merged node back to the file in order to properly load defaults. Note that I haven’t tested this but I believe it should work.
You can write the config back to the config node as simple as you can read it, but the process of actually saving the config node itself is something you still have to do.
That’s because when you use setValue(...), you need to use both the TypeToken AND the object which holds the defaults. Sorry about that, will edit original post.