【问题标题】:Understanding PHP Object Type Comparisons了解 PHP 对象类型比较
【发布时间】:2014-12-30 19:18:30
【问题描述】:

继续完成 Kent Beck 的测试驱动开发示例并用 PHP 重写示例。

第 13 章描述了一个测试,如果 2 个对象属于同一类型,则该测试应返回 true。在前几章中,评估是有效的,但是对于这个示例,我无法通过它,而且我不确定它为什么会失败。

给定一个实现 Expression 接口的类“Sum”:

    class Sum implements Expression {
    public $augend;
    public $addend;


    public function __construct($augend, $addend)
    {

        $this->augend = $augend;
        $this->addend = $addend;

    }

     // impl of Expression interface, but this smells to me, dupe implementation
    // also in Money
    public function plus($addend) {
        return new Sum($this, $addend);
    }

    public function reduce($to) {         
        $amount = $this->augend->amount + $this->addend->amount;

        return new Money($amount, $to);
    }
}

和表达式:

    interface Expression {
    public function plus($addend);

    public function reduce($to);

}

我正在尝试调用 Bank 对象(称为 reduce)的方法,该对象的第一个参数是 Sum 对象,它有自己的 reduce 实现。但是,Java 示例将第一个 arg 指定为接口,而不是具体类:

    class Bank {

    // the book defines the $source param as type Expression, which is legal 
    // in Java but not in PHP
    public function reduce($source, $to) {

       return $source->reduce($to);
    }
}

最后,我的 Money 课程:

    class Money implements Expression {
    public $amount;
    public $currency;

    public function __construct($amount, $currency) {
        $this->amount = $amount;
        $this->currency = $currency;
    }

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

    public function equals($compareObject) {

        return $this->amount == $compareObject->amount
         && $this->currency() == $compareObject->currency();
    }

    // static factory method that returns Dollar 
    // (reduces dependence on subclasses)
    static function dollar($amount) {
        return new Dollar($amount, "USD");
    }

    static function franc($amount) {
        return new Franc($amount, "CHF");
    }

    public function times($multiplier) {
        return new Money($this->amount * $multiplier, $this->currency);
    }


     // impl of Expression interface
    public function plus($addend) {
        return new Sum($this, $addend);
    }

    public function reduce($to) {
        return $this;
    }
}

运行此测试时:

$sum = new Sum(Money::dollar(3), Money::dollar(4));
$bank = new Bank();

$result = $bank->reduce($sum, "USD");

$this->assertEquals(Money::dollar(7), $result); //FAIL

断言失败,表明 $result 是 Money 类型而不是 Dollar 类型,即使我已经验证每个对象的属性都匹配:

$this->assertEquals(Money::dollar(7)->amount, $result->amount);


$this->assertEquals(Money::dollar(7)->currency, $result->currency);

这是缺乏将对象本质上转换为特定类型的能力的结果吗?我没有更改子类的实现,之前的测试仍然通过:

$five = Money::dollar(5);
$this->assertEquals(new Money(10, "USD"), $five->times(2));   
$this->assertEquals(new Money(15, "USD"), $five->times(3));
$this->assertEquals(get_class($five), "Dollar");

【问题讨论】:

    标签: php oop tdd


    【解决方案1】:

    两个不同类型的对象永远不可能相等。 PHP 不会让一个对象自己决定它是否等于另一个对象,就像 java 使用 equal-method 所做的那样。

    http://php.net/manual/en/language.oop5.object-comparison.php

    在您的测试用例中,您可以自己调用 eqauls 方法。

    $this->assertsTrue( $result->equals(Money::dollar(7)) );
    

    【讨论】:

    • 我最终这样做了 - $this->assertTrue(Money::dollar(7)->equals($result)); 评估结果为 true 但对我来说感觉很脆弱,因为我依赖于我自己的对象相等实现,而不是一些内在的 PHP 方法(这显然没有'反正不存在)
    【解决方案2】:

    这更像是一个架构问题而不是使用 phpunit 的问题。我给了你 2 个简单的解决方案,但你当然应该用一些设计模式来改进你的代码。

    1.

    如果必须将“USD”传递给构造函数,那么定义类Dollar 的意义何在?请改用 Money 类。

    // static factory method that returns Dollar 
    // (reduces dependence on subclasses)
    static function dollar($amount) {
        return new Money($amount, "USD");
    }
    
    static function franc($amount) {
        return new Money($amount, "CHF");
    }
    
    public function times($multiplier) {
        return new Money($this->amount * $multiplier, $this->currency);
    }
    

    2.

    Dollar 的总和返回 Money 可能是另一种货币(如果您没有转换机制),这不是逻辑。您可以像这样更改Sum 的方法reduce

    public function reduce() {         
        $money = clone $this->augend;
        $money->amount = $this->augend->amount + $this->addend->amount;
    
        return $money;
    }
    

    ---

    使用这些解决方案,您的测试将按预期进行:

    $sum = new Sum(Money::dollar(3), Money::dollar(4));
    $bank = new Bank();
    
    $result = $bank->reduce($sum, "USD");
    
    $this->assertEquals(Money::dollar(7), $result); // OK
    

    因为这个断言检查对象的类和属性的相等性。

    【讨论】:

    • 虽然你可能有一个有效的观点,但我不能假设这不会在书中完成,因为我只完成了大约 2/3,而贝克确实很重要全程重构。
    猜你喜欢
    • 2010-10-17
    • 2021-12-31
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多