oop — Понимание сравнений типов объектов PHP

Продолжаем работать над тест-ориентированной разработкой Кента Бека на примерах и переписываем примеры на PHP.

Глава 13 описывает тест, который должен возвращать true, если 2 объекта одного типа. В предыдущих главах оценка работала, но для этого одного примера я не могу выполнить это, и я не уверен, почему это не удается.

Имеется класс «Sum», который реализует интерфейс Expression:

    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, который имеет собственную реализацию Redu. Тем не менее, пример Java указывает первый аргумент как интерфейс, а не конкретный класс:

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

1

Решение

Два объекта разного типа никогда не могут быть равны. PHP не позволяет объекту самому решать, равен ли он другому объекту, как это делает Java с помощью метода equal.

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

В ваших тестовых случаях вы можете вызвать метод eqauls самостоятельно.

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

Другие решения

Это скорее проблема архитектуры, чем проблема использования phpunit. Я дал вам 2 простых решения, но вы, конечно, должны улучшить свой код с помощью некоторых шаблонов проектирования.

1.

Какой смысл определять класс Dollar если вы должны передать «USD» конструктору? Используйте класс 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 возможно с другой валютой (если у вас нет механизма конвертации). Вы можете изменить метод reduce из Sum как это:

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

Потому что это утверждение проверяет класс объектов и равенство свойств.

0

По вопросам рекламы [email protected]