In case you are working with the ES6+ class system, you most likely want to use configs (properties) for your own classes. Since the class system is still lacking to define these inline, you could use the constructor:
You will most likely notice right away, that this approach is not really helpful, since you have no change listeners for your class configs. Defining the properties via get & set can help with this:
Both class definitions are pretty similar, but the 2nd one allows us to use myInstance.a = 2; for automatically triggering the setter. As a design pattern, saving the content of each property inside a underscore prefixed version does make sense (e.g. a => a). You could add more logic into each setter, but as soon as you want to extend your own classes again, this will cause problems. So calling new methods which you can extend or override makes more sense:
Passing the new & the old value makes a lot of sense. As a design pattern, I recommend to use method names like afterSetMyConfig(). In case you want to extend your class now, you can override afterSetA and use the superclass call (super.afterSetA(value, oldValue);) as needed.
In case you are using the neo.mjs framework, you can shorten this code quite a bit:
applyClassConfig will generate something similar to the previous version, with the difference that you can change the default values for your properties more easily when extending classes.
Chicken or egg?
You might have noticed that afterSetA() is using this.b internally. You could flip the order of a & b_ inside the getConfig() method to change the order in which the configs do get applied. Since afterSetB() is using this.a internally, we ended up with a circular reference, so we can not solve this issue just using the config order. In neo.mjs, i solved it like this:
The important part is that in case not all configs got applied yet, the afterSet methods will not get executed but get stored inside an afterSetQueue instead. Once all configs did get applied, the afterSetQueue will get processed and this ensures that circular references when creating classes / instances are no longer a problem.
How to define an instance state?
I have seen several different approaches and personally prefer to see a state as a permutation of class configs (properties).
After creating an instance (you can use “new” if not working with the neo.mjs class system), you can change each config directly as an assignment, triggering the setter:
In our simple TestClass example, a state could be:
and another one could be:
To switch from the initial state to the second one, you could use:
Chicken or egg version 2
Although you can flip the order of the configs, we are still dealing with our circular dependency inside the afterSet methods, so using Object.assign() might cause issues.
The solution is to create a set() method, which will use the afterSetQueue for dynamic changes as well:
You can define the logic like this, in case you don’t want to use neo.mjs:
A fully working test could look like this one:
Running it will generate the following results: