Normally when I want to create a class constructor that accepts different types of parameters, I’ll use a kludgy overloading principle of not defining any args in the constructor definition:
e.g. for an ECEF coordinate class constructor, I want it to accept either $x, $y and $z arguments, or to accept a single array argument containg x, y and z values, or to accept a single LatLong object I’d create a constructor looking something like:
function __construct()
{
// Identify if any arguments have been passed to the constructor
if (func_num_args() > 0) {
$args = func_get_args();
// Identify the overload constructor required, based on the datatype of the first argument
$argType = gettype($args[0]);
switch($argType) {
case 'array' :
// Array of Cartesian co-ordinate values
$overloadConstructor = 'setCoordinatesFromArray';
break;
case 'object' :
// A LatLong object that needs converting to Cartesian co-ordinate values
$overloadConstructor = 'setCoordinatesFromLatLong';
break;
default :
// Individual Cartesian co-ordinate values
$overloadConstructor = 'setCoordinatesFromXYZ';
break;
}
// Call the appropriate overload constructor
call_user_func_array(array($this,$overloadConstructor),$args);
}
} // function __construct()
I’m looking at an alternative: to provide a straight constructor with $x, $y and $z as defined arguments, and to provide static methods of createECEFfromArray() and createECEFfromLatLong() that handle all the necessary extraction of x, y and z; then create a new ECEF object using the standard constructor, and return that
Which option is cleaner from an OO purists perspective?
I’ve been thinking of the suggestions offered here and by others to clean up the overloading of the object constructor. The method I’ve decided upon is, I think, OO elegant, simple to implement and intuitive to use.
As a solution, I decided to implement a common interface for the constructor arguments:
I began by creating an interface.
I added an abstract to implement the getters defined in the interface, together with setters and a few other methods that would be common across several child classes.
For my main class constructor, I type hinted for it to accept classes that extended the interface definition:
Finally, I created a couple of classes extending my abstract that would each handle the different options for the constructor arguments; in this case, an array of values, and individual values… I’ll write the LatLong variant later, but it will use the same basic principles and extend Geodetic_ECEF_Coordinates in the same way.
So now, when I instantiate an ECEF object, I pass it the appropriate Geodetic_XyzFormat object:
It doesn’t require all the kludgy tests for different argument types (whether in a factory, or in my main class), nor the use of statics (so it should be pretty easy writing unit tests for it)
Thanks for everyone who offered suggestions and gave me food for thought