Given e.g. of the Pseudo Constructor in CFML:
component{
// Pseudo Constructor start
... here comes some cfml scripting code..
// Pseudo Constructor end
function init(){
return this;
}
}
I already understand that the Pseudo Constructor in a .cfc component:
Please consider that I'm not refering to the use of cfproperty
or property
here, but to any other code in the Pseudo Constructor.
Still, I've not seen a good example or use case in CFML to help me decide. Could any experienced OOP CFML developer elaborate an example to understand it better:
Please give an example code in such a manner that may help me and others decide when to use one over the other in the future?
After investigating a little deeper I came up with my own conclusion that I'd like to share with all interrested CFML developers. If any experienced OOP CFML developer has more precise information, I'd be really glad to know.
I've created a component named Cube.cfc that may answer the question for itself. My conclusion basically is that "pseudo constructors" allow to create some kind of "static" functions, because these fucntions can make use of these variables without instantiation of an object. Note that I didn't want to use the term "static" because these functions lacks of the naming attribute "static" (as far as I'm aware, only Lucee supports real "static" functions at the moment). Also, the example I'm showing only seems to work with createObject() and not the implicit constructors e.g. new Component(), because new Component() would instantiate the object immediately. But: using createObject and the pseudo constructor will at least allow to mimic static functions. Please note that my example component is just to be descriptive.
In the following example im using available functions that won't need any object instantiation. These functions can be used to retrieve some usefull informations that are not bound to any created/instantiated object.
Cube.cfc: a simple component to create cube objects
component displayname="Cube" accessors ="true" {
// class properties
property name="name" type="string";
property name="model" type="string";
property name="borderColor" type="string";
property name="material" type="string";
property name="dimension" type="struct";
// pseudo constructor
variables.models =[
"model-a",
"model-b",
"model-c"
];
variables.modelNameMaterialMapping ={
"model-a": "wood",
"model-b": "steel",
"model-c": "silver"
};
variables.modelNameDimensionsMapping ={
"model-a": {"height": 100, "length": 100, "width": 100 },
"model-b": {"height": 133, "length": 133, "width": 133 },
"model-c": {"height": 85, "length": 85, "width": 85 }
};
public any function init(
string name,
string borderColor,
string model
){
setName( arguments.name );
setBorderColor( arguments.borderColor );
setModel( arguments.model );
setMaterial( getMaterialByModelName( arguments.model ) );
setDimension( getDimensionByModelName( arguments.model ) );
return this;
}
//this function won't need any instantiating of an object because it uses variables of the pseudo constructor
public string function getMaterialByModelName( string modelName ){
return modelNameMaterialMapping[ arguments.modelName ];
}
//this function won't need any instantiating of an object because it uses variables of the pseudo constructor
public struct function getDimensionByModelName( string modelName ){
return modelNameDimensionsMapping[ arguments.modelName ];
}
//this function won't need any instantiating of an object
public string function isValidModel( string model ){
return variables.models.contains( arguments.model );
}
}
index.cfm:
<cfscript>
CubeService = CreateObject("component","Cube");
writeDump( CubeService );
writeDump( GetMetaData( CubeService ) );
modelsForChecking=[
"model-a",
"model-k",
"model-c",
"model-z"
];
// loop through model information without having any object instantiated
for( model in modelsForChecking){
if( CubeService.isValidModel( model )){
writeOutput("Cube ""#model#"" is valid.<br>");
writeOutput( "Cube models ""#model#"" are made of ""#CubeService.getMaterialByModelName( model )#"" and a dimension of ""#CubeService.getDimensionByModelName( model ).width#x#CubeService.getDimensionByModelName( model ).length#x#CubeService.getDimensionByModelName( model ).height#""<br>");
}else{
writeOutput("Cube ""#model#"" is NOT a valid model.<br>");
}
}
//intantiate a specific cube object with the name "CubeOne";
writeOutput( "Instantiate an object with the component:<br>");
CubeOne=CubeService.init("CubeOne", "white", "model-c" );
// dump properties of the specific cube "CubeOne"
writeDump( CubeOne );
// get width with the accessor getter for property "dimension" for the cube named "CubeOne"
writeOutput("""CubeOne"" has a width of #CubeOne.getDimension().width# <br>");
</cfscript>
If you run the above files you'll note that the functions:
don't need any instantiated object. They just retrieve some usefull information about the cube models without running any init() function.
That causes me to assume the following thumb rules:
Use variables that are bound to a property of an instantiated object within the "init()" functions.
Use variables in the 'Pseudo Constructor' whenever you need to use functions with those variables before having used the init() function.
Note that my component is just to be an descriptive example. If somebody comes up with more detailed information about the topic or I need to be corrected in my assumptions, I'd be glad to know.
IMPORTANT UPDATE: As @SOS thankfully commented, Adobe Coldfusion supports static functions since Coldfusion 2021. These "non-instantiated-object-related" functions can now be directly invoked with Component::staticFunctionName( args )
without using any preceeding CreateObject()
nor the implicit constructor new Component()
! As @SOS also commented, looks like "using static is probably the best approach for 2021+" in CFML because now both CFML engines Lucee and Coldfusion fully supports them.
For completeness I'm placing an adapted/rewritten version of my example code as a reference for 2021+:
Cube.cfc
component displayname="Cube" accessors ="true" {
// class properties
property name="name" type="string";
property name="model" type="string";
property name="borderColor" type="string";
property name="material" type="string";
property name="dimension" type="struct";
// set static varibales
static {
private models =[ "model-a", "model-b", "model-c" ];
private modelNameMaterialMapping ={
"model-a": "wood",
"model-b": "steel",
"model-c": "silver"
};
private modelNameDimensionsMapping ={
"model-a": {"height": 100, "length": 100, "width": 100 },
"model-b": {"height": 133, "length": 133, "width": 133 },
"model-c": {"height": 85, "length": 85, "width": 85 }
};
};
public any function init(
string name,
string borderColor,
string model
){
setName( arguments.name );
setBorderColor( arguments.borderColor );
setModel( arguments.model );
setMaterial( static.getMaterialByModelName( arguments.model ) );
setDimension( static.getDimensionByModelName( arguments.model ) );
return this;
}
public static string function getMaterialByModelName( string modelName ){
return static.modelNameMaterialMapping[ arguments.modelName ];
}
public static struct function getDimensionByModelName( string modelName ){
return static.modelNameDimensionsMapping[ arguments.modelName ];
}
public static string function isValidModel( string model ){
return static.models.contains( arguments.model );
}
}
index.cfm
<cfscript>
modelsForChecking=[
"model-a",
"model-k",
"model-c",
"model-z"
];
// loop through model information without having any object instantiated by calling static functions
for( model in modelsForChecking){
if( Cube::isValidModel( model )){
writeOutput("Cube ""#model#"" is valid.<br>");
writeOutput( "Cube models ""#model#"" are made of ""#Cube::getMaterialByModelName( model )#"" and a dimension of ""#Cube::getDimensionByModelName( model ).width#x#Cube::getDimensionByModelName( model ).length#x#Cube::getDimensionByModelName( model ).height#""<br>");
}else{
writeOutput("Cube ""#model#"" is NOT a valid model.<br>");
}
}
//intantiate a specific cube object with the name "CubeOne";
writeOutput( "Instantiate an object with the component:<br>");
CubeOne=new Cube("CubeOne", "white", "model-c" );
// dump properties of the specific cube "CubeOne"
writeDump( CubeOne );
// get width with the accesso getter for property dimension for the cube named "CubeOne"
writeOutput("""CubeOne"" has a width of #CubeOne.getDimension().width# <br>");
</cfscript>
For further reference, see:
Static functions for CFC in Lucee