3

通常、さまざまなタイプのパラメーターを受け入れるクラス コンストラクターを作成する場合は、コンストラクター定義で引数を定義しないという厄介なオーバーロードの原則を使用します。たとえば、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 を引数として定義したストレート コンストラクターを提供し、x、y、およびz; 次に、標準コンストラクターを使用して新しい ECEF オブジェクトを作成し、それを返します

オブジェクト指向の純粋主義者の観点からは、どのオプションがよりクリーンですか?

4

1 に答える 1

0

オブジェクトコンストラクターのオーバーロードをクリーンアップするために、ここで提供された提案や他の提案について考えてきました。私が決めた方法は、OO エレガントで、実装が簡単で、直感的に使用できると思います。

解決策として、コンストラクターの引数に共通のインターフェイスを実装することにしました。インターフェイスを作成することから始めました。

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

インターフェイスで定義されたゲッターを実装するための抽象を追加し、いくつかの子クラスに共通するセッターやその他のメソッドをいくつか追加しました。

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

さまざまな引数の型 (ファクトリであろうと、メイン クラスであろうと) に対するすべての厄介なテストや、静的変数の使用は必要ありません (したがって、単体テストを作成するのは非常に簡単なはずです)。

提案を提供し、私に思考の糧を与えてくれたすべての人に感謝します

于 2012-12-08T12:51:11.277 に答える