PHPで変更できないオブジェクトを作成するのは良い考えですか?
たとえば、セッターメソッドを持つ日付オブジェクトですが、それらは常にオブジェクトの新しいインスタンスを返します(日付が変更されています)。
PHPでは通常、オブジェクトが変更されると予想されるため、これらのオブジェクトは、クラスを使用する他の人を混乱させるでしょうか?
例
$obj = new Object(2);
$x = $obj->add(5); // 7
$y = $obj->add(2); // 4
不変オブジェクトは最初の作成後に変更できないため、setterメソッドを使用しても、その基本原則に反するため意味がありません。
クラスメンバーの可視性を操作し、魔法の__set()メソッドをオーバーライドすることで、PHP)の不変性をシミュレートするためのいくつかの回避策を実装できますが、不変性は言語の機能ではないため、不変性は保証されません。かつて、PHPで不変の値の型を提供する拡張機能を作成したので、それをグーグルで検索できます。
不変オブジェクトにはsetterメソッドがありません。限目。
誰もがsetXyz()
メソッドにvoidの戻り値の型があることを期待します(または緩く型付けされた言語では何も返しません)。不変オブジェクトにsetterメソッドを追加すると、人々の地獄を混乱させ、醜いバグにつながります。
私の意見では、オブジェクトは値オブジェクトに対して不変でなければなりません。それ以外は、アプリケーション全体でオブジェクトを共有しない限り、あまりメリットはありません。
ここにはいくつかの間違った答えがあります不変オブジェクトはセッターを持つことができます。 PHPでの不変オブジェクトの実装を次に示します。
例1。
class ImmutableValueObject
{
private $val1;
private $val2;
public function __construct($val1, $val2)
{
$this->val1 = $val1;
$this->val2 = $val2;
}
public function getVal1()
{
return $this->val1;
}
public function getVal2()
{
return $this->val2;
}
}
一度インスタンス化するとわかるように、値を変更することはできません。
例2:setters
を使用:
class ImmutableValueObject
{
private $val1;
private $val2;
public function __construct($val1, $val2)
{
$this->val1 = $val1;
$this->val2 = $val2;
}
public function getVal1()
{
return $this->val1;
}
public function withVal1($val1)
{
$copy = clone $this;
$copy->val1 = $val1;
return $copy; // here's the trick: you return a new instance!
}
public function getVal2()
{
return $this->val2;
}
public function withVal2($val2)
{
$copy = clone $this;
$copy->val2 = $val2;
return $copy;
}
}
いくつかの実装が可能であり、これは決して排他的なリストではありません。また、Reflectionを使用すると、PHPでそれを回避する方法が常にあるため、最終的には不変性が頭の中にあることを忘れないでください。
不変オブジェクトをfinalとして配置することもよくあります。
編集:
不変性の実装を容易にするためにReflectionを使用しないように少し特性を作成しました: https://github.com/jclaveau/php-immutable-trait
明らかに、これは言語機能ではないため、魔法による突然変異を弾劾することはありませんが、適用される前に現在のインスタンスのクローンを作成する必要があるミューテーターのコードを軽減します。 Massimilianoの例に適用すると、
class ImmutableValueObject
{
use JClaveau\Traits\Immutable;
private $val1;
private $val2;
public function __construct($val1, $val2)
{
$this->val1 = $val1;
$this->val2 = $val2;
}
public function getVal1()
{
return $this->val1;
}
public function withVal1($val1)
{
// Just add these lines at the really beginning of methods supporting
// immutability ("setters" mostly)
if ($this->callOnCloneIfImmutable($result))
return $result;
// Write your method's body as if you weren't in an Immutable class
$this->val1 = $val1;
return $this;
}
public function getVal2()
{
return $this->val2;
}
public function withVal2($val2)
{
if ($this->callOnCloneIfImmutable($result))
return $result;
$this->val2 = $val2;
return $this;
}
}
それがここの何人かの人々を助けることができることを願っています!
PS:この小さな機能に関する提案は大歓迎です:): https://github.com/jclaveau/php-immutable-trait/issues
クラスとオブジェクトにセッターが必要な場合、これはまったく問題ありません。オブジェクトデータを設定する必要があるため、これを常に実行します。単に不変とは呼ばないでください。
開発の世界の多くのものは主観的です-私たちのアプローチ、方法論など-しかし「不変」はかなり堅実な定義です:
"不変":
-時間の経過とともに変化しないか、変更できません。
不変オブジェクトが必要な場合は、インスタンス化後に変更できないことを意味します。これは、DBからのデータなど、サイクルの期間中、石に固定しておく必要があるものに適しています。
インスタンス化後にオブジェクトを呼び出してデータを設定または変更する必要がある場合、これは不変のオブジェクトではありません。
車から2つの車輪を外して、それをバイクと呼びますか?
「set」という単語なしで名前が付けられた「不変」クラスのメソッドについての話がいくつかありますが、これはデータを設定するメソッドであるメソッドの機能を停止するものではありません。これをthisDoesNotSetAnything(int $id)
と呼び、オブジェクトを変更するデータを渡すことができます。セッターになるため、オブジェクトは変更可能です。
不変オブジェクトからその値を取得できますが、それらを変更する方法はありません。ここでは、不変クラスの例を見ることができます。
<?php
declare(strict_types=1);
final class Immutable
{
/** @var string */
private $value;
public static function withValue(string $value): self
{
return new self($value);
}
public function __construct(string $value)
{
$this->value = $value;
}
public function value(): string
{
return $this->value;
}
}
// Example of usage:
$immutable = Immutable::withValue("my value");
$immutable->value();