3

通常,当我想创建一个接受不同类型参数的类构造函数时,我将使用不定义任何参数的 kludgy 重载原则在构造函数定义中:例如,对于 ECEF 坐标类构造函数,我希望它接受 $x , $y 和 $z 参数,或接受包含 x、y 和 z 值的单个数组参数,或接受单个 LatLong 对象,我将创建一个类似于以下内容的构造函数:

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()

我正在寻找一种替代方法:提供一个直接的构造函数,其中 $x、$y 和 $z 作为定义的参数,并提供 createECEFfromArray() 和 createECEFfromLatLong() 的静态方法来处理所有必要的 x、y 和z; 然后使用标准构造函数创建一个新的 ECEF 对象,并返回它

从 OO 纯粹主义者的角度来看,哪个选项更干净?

4

1 回答 1

0

我一直在考虑这里和其他人提供的清理对象构造函数重载的建议。我认为,我决定的方法是面向对象的优雅、易于实现且使用直观。

作为一种解决方案,我决定为构造函数参数实现一个通用接口:我首先创建一个接口。

interface Geodetic_XyzFormat
{
    public function getX();
    public function getY();
    public function getZ();
}

我添加了一个抽象来实现接口中定义的 getter,以及 setter 和其他一些在多个子类中通用的方法。

abstract class Geodetic_ECEF_Coordinates implements Geodetic_XyzFormat
{
    protected $_xCoordinate;
    protected $_yCoordinate;
    protected $_zCoordinate;

    protected function setX($xCoordinate)
    {
        $this->_xCoordinate = $xCoordinate;
    }

    public function getX()
    {
        return $this->_xCoordinate;
    }

    protected function setY($yCoordinate)
    {
        $this->_yCoordinate = $yCoordinate;
    }

    public function getY()
    {
        return $this->_yCoordinate;
    }

    protected function setZ($zCoordinate)
    {
        $this->_zCoordinate = $zCoordinate;
    }

    public function getZ()
    {
        return $this->_zCoordinate;
    }

    protected function setCoordinates($xDistance,
                                      $yDistance,
                                      $zDistance,
                                      $uom)
    {
        $this->setX(
            ($xDistance instanceof Geodetic_Distance) ? $xDistance : new Geodetic_Distance($xDistance, $uom)
        );

        $this->setY(
            ($yDistance instanceof Geodetic_Distance) ? $yDistance : new Geodetic_Distance($yDistance, $uom)
        );

        $this->setZ(
            ($zDistance instanceof Geodetic_Distance) ? $zDistance : new Geodetic_Distance($zDistance, $uom)
        );
    }

}

对于我的主类构造函数,我键入提示它接受扩展接口定义的类:

class Geodetic_ECEF_TestClass
{
    protected $_xCoordinate;
    protected $_yCoordinate;
    protected $_zCoordinate;

    public function __construct(Geodetic_XyzFormat $xyzCoordinates = NULL)
    {
        if (!is_null($xyzCoordinates)) {
            $this->_xCoordinate = $xyzCoordinates->getX();
            $this->_yCoordinate = $xyzCoordinates->getY();
            $this->_zCoordinate = $xyzCoordinates->getZ();
            return;
        }

        //    Defaults
          $this->_xCoordinate = new Geodetic_Distance();
           $this->_yCoordinate = new Geodetic_Distance();
           $this->_zCoordinate = new Geodetic_Distance();
    }
}

最后,我创建了几个类来扩展我的抽象,每个类都可以处理构造函数参数的不同选项;在这种情况下,一个值数组和单个值...稍后我将编写 LatLong 变体,但它将使用相同的基本原理并以相同的方式扩展 Geodetic_ECEF_Coordinates。

class Geodetic_ECEF_CoordinateArray extends Geodetic_ECEF_Coordinates
{
    public function __construct(array $coordinates = NULL, $uom = Geodetic_Distance::METRES)
    {
        if (is_null($coordinates))
            throw new Geodetic_Exception('An array of vector coordinates must be passed');
        if (count($coordinates) == 3) {
            list ($xDistance, $yDistance, $zDistance) = array_values($coordinates);
        } else {
            throw new Geodetic_Exception('Invalid number of vectors in array');
        }

        $this->setCoordinates($xDistance, $yDistance, $zDistance, $uom);
    }

}


class Geodetic_ECEF_CoordinateValues extends Geodetic_ECEF_Coordinates
{
    public function __construct($xDistance = NULL,
                                $yDistance = NULL,
                                $zDistance = NULL,
                                $uom = Geodetic_Distance::METRES)
    {
        $this->setCoordinates($xDistance, $yDistance, $zDistance, $uom);
    }

}

所以现在,当我实例化一个 ECEF 对象时,我将它传递给适当的 Geodetic_XyzFormat 对象:

//    Nothing passed to constructor
$dummyECEF1 = new Geodetic_ECEF_TestClass();
var_dump($dummyECEF1);

//    Array of values passed to constructor
$dummyECEF2 = new Geodetic_ECEF_TestClass(
    new Geodetic_ECEF_CoordinateArray(
        array(1.2, 3.4, 5.6)
    )
);
var_dump($dummyECEF2);

//    Individual values passed to constructor
$dummyECEF3 = new Geodetic_ECEF_TestClass(
    new Geodetic_ECEF_CoordinateValues(7.8, 9.1, 2.3)
);
var_dump($dummyECEF3);

//    Individual values passed to constructor (including a NULL, which should be treated as a 0)
$dummyECEF4 = new Geodetic_ECEF_TestClass(
    new Geodetic_ECEF_CoordinateValues(4.5, NULL, 6.7)
);
var_dump($dummyECEF4);

$xDistance = new Geodetic_Distance(11.11, Geodetic_Distance::MILES);
$yDistance = new Geodetic_Distance(22.22, Geodetic_Distance::MILES);
$zDistance = new Geodetic_Distance(33.33, Geodetic_Distance::MILES);
//    Array of distances passed to constructor
$dummyECEF5 = new Geodetic_ECEF_TestClass(
    new Geodetic_ECEF_CoordinateArray(
        array($xDistance, $yDistance, $zDistance)
    )
);
var_dump($dummyECEF5);

$xDistance = new Geodetic_Distance(44.44, Geodetic_Distance::MILES);
$yDistance = new Geodetic_Distance(55.55, Geodetic_Distance::MILES);
$zDistance = new Geodetic_Distance(66.66, Geodetic_Distance::MILES);
//    Individual distances passed to constructor
$dummyECEF6 = new Geodetic_ECEF_TestClass(
    new Geodetic_ECEF_CoordinateValues($xDistance, $yDistance, $zDistance)
);
var_dump($dummyECEF6);

$xDistance = new Geodetic_Distance(11.11, Geodetic_Distance::MILES);
$yDistance = new Geodetic_Distance(22.22, Geodetic_Distance::KILOMETRES);
$zDistance = new Geodetic_Distance(33.33, Geodetic_Distance::MILES);
//    Array of mixed values and distances passed to constructor
$dummyECEF7 = new Geodetic_ECEF_TestClass(
    new Geodetic_ECEF_CoordinateArray(
        array(11, $yDistance, 33), 
        Geodetic_Distance::MILES
    )
);
var_dump($dummyECEF7);

$xDistance = new Geodetic_Distance(44.44, Geodetic_Distance::MILES);
$yDistance = new Geodetic_Distance(55.55, Geodetic_Distance::KILOMETRES);
$zDistance = new Geodetic_Distance(66.66, Geodetic_Distance::INCHES);
//    Individual mixture of distances and values passed to constructor
$dummyECEF8 = new Geodetic_ECEF_TestClass(
    new Geodetic_ECEF_CoordinateValues($xDistance, 55, $zDistance, Geodetic_Distance::NAUTICAL_MILES)
);
var_dump($dummyECEF8);

它不需要针对不同参数类型(无论是在工厂中还是在我的主类中)的所有 kludgy 测试,也不需要使用静态(因此为它编写单元测试应该很容易)

感谢所有提供建议并让我深思的人

于 2012-12-08T12:51:11.277 回答