Lazy Initialization
Context
An internal, application specific set of design rules in the implementation of
TyRuBa.
These rule ensure that the instance variables of
SomeSpecificClass are properly initialized
before being used.
While this rule is specific to a particular class in the
TyRuBa
code base and governs the internal structure of that class, it is likely that similar structural idioms are used elsewhere (to lazyly create
PreparedInsert instances used by a class.
Description
In the class
tyRuBa.engine.MetaBase
the following design rules are adopted to govent orderly initialization of fields that hold an instantce of
PreparedInsert
.
- The fields that hold an instance of PreparedInsert should match the naming pattern
*Fact
.
- The
*Fact
fields should be set to null
initially.
- Any method that uses a
*Fact
field must call the method lazyInitialize
first.
- All
*Fact
fields are initialized in lazyInitialized
.
Rationale
Following this group of rules dictates exactly how and where the
*Fact*
should get initialized and makes it easy to verify that indeed they are initialized before they are used.
Example
public class MetaBase {
private PreparedInsert typeConstructorFact = null;
private PreparedInsert nameFact = null;
private PreparedInsert subtypeFact = null;
private void lazyInitialize() {
if (typeConstructorFact==null)
try {
typeConstructorFact = engine.prepareForInsertion("meta.type(!t)");
nameFact = engine.prepareForInsertion("meta.name(!t,!n)");
subtypeFact = engine.prepareForInsertion("meta.subtype(!super,!sub)");
} catch (ParseException e) {
e.printStackTrace();
throw new Error(e);
} catch (TypeModeError e) {
e.printStackTrace();
throw new Error(e);
}
}
public void assertTypeConstructor(TypeConstructor type) {
lazyInitialize();
try {
typeConstructorFact.put("!t",type);
typeConstructorFact.executeInsert();
nameFact.put("!t",type);
nameFact.put("!n",type.getName());
nameFact.executeInsert();
type.setMetaBase(this);
} catch (TyrubaException e) {
throw new Error(e);
}
}
...
Definition
These rules can be partly verified by JQuery.
The following queries can be run on the
MetaBase
class (i.e. with
?this
bound to the
MetaBase
class) to verify some parts of the desing rule.
The following query detects methods that access a
*Fact*
field but do not call
lazyInitialize
:
reads(?M,?f,?), re_name(?f,/Fact$/),
NOT( EXIST ?lazyInit :
name(?lazyInit,lazyInitialize), calls(?M,?lazyInit) )
This rule is weaker than our actual design rule: it does not guarantee that the lazyInitialize is called
before accessing the field. However, this is a good enough rule in that it ensures that at least the developer did not forget the call. It is unlikely in this case the call is not in the right place (at the beginning of the method).
The following query ensures that
all *Fact
fields are initialized in the
lazyInitialize
method
(so they are not accidentally forgotten).
field(?this,?f),re_name(?f,/Fact$/),
name(?writer,lazyInitialize),
NOT( writes(?writer,?f,?) )
The following query finds violations of the naming convention for Fact fields:
field(?this,?f),type(?f,?PrepIns),name(?PrepIns,PreparedInsert), NOT( re_name(?f,/Fact$/) )
The following query ensures finds if their are any field writes in the wrong places (i.e. outside of field initializers where they are supposed to be set to null, or outside of
lazyInitialize
method.
field(?this,?f),re_name(?f,/Fact$/),
writes(?writer,?f,?loc),
NOT( Initializer(?writer) ;
name(?writer,lazyInitialize) )
This rule is also weaker than the actual design rule: it does not verify that a null value is assigned in the initializer or a non-null value is assigned in the
lazyInitialize
method.
I don't know if this can be verified by
MetaL. But it is possible
MetaL may be able to verify a more general constraint that nothing should be used before it is initialized.
AspectJ can express some of the same things that JQuery can but not all of them.
- can verify that field writes appear in the right contexts only (I think).
- cannot verify that all fields are properly initialized inside of
lazyInitialize
(AspectJ can say what is not allowed but can not say what is mandatory).
- AspectJ can enforce the Fact field naming convention (I think).
naming convention
forall field(PreparedInsert MetaBase.*) => nameMatch(*Fact)
Calls lazyInitialize:
forall get(MetaBase.*Fact)
always precededby( call(MetaBase.lazyInitialize()) )
No initialization in wrong place:
forall ?f=field(MetaBase.*Fact) && set(?f)
always withincode(MetaBase.lazyInitialize()) || withincode( initializer ?f )
Ruminations
-- Main.kdvolder - 05 May 2005