In some cases it would be beneficial to run some code after all constructors, but before an object is ready for use. Some valid examples:
- Deleting objects and freeing resources, which were required for initialization, but have no use after that.
- Creating new objects and allocating new resources, which depend on combined work of other constructors.
The problem here is that with mixins we have no idea when constructors stop running: after any given constructor another one can be weaved.
Let’s illustrate it with a simple example: templating. Imagine that our object has a template, which should be populated by properties on an instance. Those properties are added by constructors. So let’s create a mixin for that:
1 2 3 4 5 6 7 8 9 10
Looks perfect: simple yet flexible. If
fullName is “Robert Smith”, following
result will be created:
The only question is when to call it.
Solution #1 (fail)
Let’s call it from our constructor:
1 2 3 4 5 6 7
While it looks okay, it will fail: a template may contain calculated properties (e.g.,
fullName can be a combination of
lastName), and there is no guarantee
that their values are calculated in constructors that run before our
are we will look at “Hello, undefined!”.
Solution #2 (epic fail!)
Let’s be uber-smart and call the method using
1 2 3 4 5 6 7 8 9 10 11
The thinking goes like that:
Constructors run and at some point they all finish. So if we schedule our call for a next time slice, we are guaranteed to run after all constructors.
Next time slice? I set a timer for 0ms, so my code will be called immediately without any wait.
Unfortunately this thinking, while prevalent, fails on several accounts:
- “0ms timeout” is not free, and not exactly equals to “next time slice”. Read all about it in More on 0ms timeouts.
- What if as soon as we constructed an object we started to use it without waiting for time slices? Making this condition part of a contract (e.g., by documenting it) makes use of such objects cumbersome.
Solution #3 (just works)
Use AOP. Schedule to run the method using “after” advice:
1 2 3 4 5 6 7
This way it is guaranteed that it will run after all other constructors, and
without any delay.
Template can be mixed in any order with other mixins, yet
its code will always run after “proper” constructors.
Never underestimate the power of AOP techniques. They are here to make programmer’s life
easier. That’s why
dcl supports both dynamic (object-level) and static (class-level)
Just like with an “after” advice, it is possible to prepare for construction with “before” advices, or use similar techniques with regular methods. Frequently an AOP solution is much more elegant and robust than alternative hacks.