web-dev-qa-db-ja.com

PHPの不変オブジェクト?

PHPで変更できないオブジェクトを作成するのは良い考えですか?

たとえば、セッターメソッドを持つ日付オブジェクトですが、それらは常にオブジェクトの新しいインスタンスを返します(日付が変更されています)。

PHPでは通常、オブジェクトが変更されると予想されるため、これらのオブジェクトは、クラスを使用する他の人を混乱させるでしょうか?

$obj = new Object(2);

$x = $obj->add(5); // 7
$y = $obj->add(2); // 4
13
Elfy

不変オブジェクトは最初の作成後に変更できないため、setterメソッドを使用しても、その基本原則に反するため意味がありません。

クラスメンバーの可視性を操作し、魔法の__set()メソッドをオーバーライドすることで、PHP)の不変性をシミュレートするためのいくつかの回避策を実装できますが、不変性は言語の機能ではないため、不変性は保証されません。かつて、PHPで不変の値の型を提供する拡張機能を作成したので、それをグーグルで検索できます。

6
pipe-devnull

不変オブジェクトにはsetterメソッドがありません。限目。

誰もがsetXyz()メソッドにvoidの戻り値の型があることを期待します(または緩く型付けされた言語では何も返しません)。不変オブジェクトにsetterメソッドを追加すると、人々の地獄を混乱させ、醜いバグにつながります。

25
ThiefMaster

私の意見では、オブジェクトは値オブジェクトに対して不変でなければなりません。それ以外は、アプリケーション全体でオブジェクトを共有しない限り、あまりメリットはありません。

ここにはいくつかの間違った答えがあります不変オブジェクトはセッターを持つことができます。 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として配置することもよくあります。

編集:

  • withXのsetXを変更しました
  • ファイナルについてのコメントを追加
11
Théo

不変性の実装を容易にするために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;
    }
}
  • ここでは$ copyを返さないが、KanstantsinKが気付いたように$ thisを返すことがわかります。
  • ネイティブではPHP https://secure.php.net/manual/en/class.datetimeimmutable.php 変更が適用された新しいインスタンスを返すミューテーターがあります。不変オブジェクトにミューテーターを含めるべきではないという文章をコピーして貼り付けるのは、あまり面白くないようです。
  • 「setXXX」の代わりに「withXXX」を使用する方法は非常に興味深いものです。提案に感謝します。私は個人的に、インスタンスの可変性をチェーンするAPIに「becomesXXX」を使用しました(トレイトSwitchableMutabilityのオプションのAPI)。

それがここの何人かの人々を助けることができることを願っています!

PS:この小さな機能に関する提案は大歓迎です:): https://github.com/jclaveau/php-immutable-trait/issues

0
Meuoi

クラスとオブジェクトにセッターが必要な場合、これはまったく問題ありません。オブジェクトデータを設定する必要があるため、これを常に実行します。単に不変とは呼ばないでください。

開発の世界の多くのものは主観的です-私たちのアプローチ、方法論など-しかし「不変」はかなり堅実な定義です:

"不変":
-時間の経過とともに変化しないか、変更できません。

不変オブジェクトが必要な場合は、インスタンス化後に変更できないことを意味します。これは、DBからのデータなど、サイクルの期間中、石に固定しておく必要があるものに適しています。

インスタンス化後にオブジェクトを呼び出してデータを設定または変更する必要がある場合、これは不変のオブジェクトではありません。

車から2つの車輪を外して、それをバイクと呼びますか?

「set」という単語なしで名前が付けられた「不変」クラスのメソッドについての話がいくつかありますが、これはデータを設定するメソッドであるメソッドの機能を停止するものではありません。これをthisDoesNotSetAnything(int $id)と呼び、オブジェクトを変更するデータを渡すことができます。セッターになるため、オブジェクトは変更可能です。

0
James

不変オブジェクトからその値を取得できますが、それらを変更する方法はありません。ここでは、不変クラスの例を見ることができます。

<?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(); 
0
Chemaclass