Following on from the previous discussion about OOP principles in Adapt, Fabien, Gavin McMaster (a fellow programmer at Kineo) and myself have now developed an OOP framework which I am optimistic about.
I understand there is some concern in the community about the flaky nature of using 'super' in JavaScript. This concern is well-founded; infinite recursion, loss of scope, single-level inheritance - these are all hurdles that need jumping for anyone wanting to achieve a semblance of OOP principles in JavaScript.
I am confident that we've managed to successfully jump those hurdles in a way that:
- Is easy for any developer to implement
- Creates a robust plugin-ready architecture
- Greatly reduces code duplication
I have conducted some unit tests, attached here, and am very interested in hearing if anyone else has some ideas of what they'd want to see tested.
When would we use this?
It's important to note that there are two mechanics at work here: extend and mutate. While very similar, these two functions serve different purposes.
Extend
This is used in a way that will be familiar to those used to working in an Object-oriented language. You can define an object, and then extend that object to create 'child' objects, which inherit methods and properties from their parent. They are able to override parent methods, but have access to those methods by simply calling _super(). Parameters can be passed in to the function call as per normal.
This would be used both in the core and potentially in plugins too.
An example inheritance chain in Adapt's core:
AbstractView > ComponentView > QuestionView > MCQView > GMCQView
Mutate
This is similar to extend, but instead of creating a new object which inherits from a 'super' object, you can, in a non-destructive and non-invasive way, modify an object without completely rewriting it. The makes it perfect for creating plugins which need to modify some element of the core behaviour without ever needing to touch the core code.
Using the example above, a plugin could 'mutate' ComponentView, and all the child objects will then inherit any mutated functions and properites.
AbstractView > ComponentView---MutatedComponentView > Question > MCQ etc
The great thing about this is that mutated object functions can call their original, non-mutated counterparts by calling _original().
An example plugin which mutates one of the core objects is the 'triggered' plugin. This overrides ComponentView's "initialise" and "render" functions. Instead of writing those out in full and adding new functionality, it simply calls this._original() and then adds the new code in the lines which follow.
It'd be good to get some feedback on this. To run the unit tests attached, simply launch testing/mutate_extend/tests.html