17

これはおそらく主観的であることはわかっていますが、Google から PHP 用のこの最適化ページを読んだところ、ゲッターとセッターを必要とせずに変数プロパティを直接使用することが提案されています。当然のことながら、これでパフォーマンスが向上することがわかりますが、これは本当に従うべき良い設計プラクティスですか?

ゲッター/セッターを使用した例:

class dog {
  public $name = '';

  public function setName($name) {
    $this->name = $name;
  }

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

$rover = new dog();
$rover->setName('rover');
echo $rover->getName();

推奨される最適化:

$rover = new dog();
$rover->name = 'rover';
echo $rover->name;

ゲッター/セッターの必要性がなくなるので、これは私の設計プロセスの歓迎すべき変更ですが、これを行うことで他にどのようなハードル/利点が発生する可能性がありますか?

4

7 に答える 7

12

ゲッター/セッターの必要性がなくなるので、これは私の設計プロセスの歓迎すべき変更ですが、これを行うことで他にどのようなハードル/利点が発生する可能性がありますか?

特定のプロパティに特別な get/set ロジックを実装できなくなります。スカラー (文字列、整数、ブール値) であるプロパティの場合、これはおそらく問題ありません。しかし、遅延ロードされたクラス インスタンスであるプロパティがある場合はどうなるでしょうか。

class Document
{
    protected $_createdBy;

    public function getCreatedBy()
    {
        if (is_integer($this->_createdBy)) {
            $this->_createdBy = UserFactory::loadUserById($this->_createdBy);
        }
        return $this->_createdBy;
    }
}

そのトリックはメソッドでのみ機能します。__getこのロジックに andを使用できます__setが、プロパティを追加すると、大きな厄介なswitch()ブロックになります。

public function __get($name)
{
    switch ($name) {
        case 'createdBy':
            // blah blah blah
        case 'createdDate':
            // more stuff
        // more case statements until you scream
    }
}

getter と setter の記述を避けたい、または先延ばしにしたい場合は、マジック メソッドを使用して、命名規則__callに従うメソッド呼び出しをトラップします。デフォルトの get/set ロジックをすべて入れて、二度と触れないようにすることができます。getProperty()setProperty()__call

abstract class Object
{
    public function __call($method, $args)
    {
        $key = '_' . strtolower(substr($method, 3, 1)) . substr($method, 4);
        $value = isset($args[0]) ? $args[0] : null;
        switch (substr($method, 0, 3)) {
            case 'get':
                if (property_exists($this, $key)) {
                    return $this->$key;
                }
                break;

            case 'set':
                if (property_exists($this, $key)) {
                    $this->$key = $value;
                    return $this;
                }
                break;

            case 'has':
                return property_exists($this, $key);
                break;
        }

        throw new Exception('Method "' . $method . '" does not exist and was not trapped in __call()');
    }
}

Object クラスを拡張し、いくつかのプロパティを定義するだけでレースに参加できるため、このアプローチは開発の観点から非常に高速です。

class Foo extends Object
{
    protected $_bar = 12345;
}

$foo = new Foo();
echo $foo->getBar();  // outputs '12345'
$foo->setBar(67890);  // next call to getBar() returns 67890
$foo->getBaz();       // oops! 'baz' doesn't exist, exception for you

魔法のメソッドは非常に遅いため、実行の観点からは遅いですが、後で明示的なメソッドgetBar()setBar()メソッドを定義することで軽減できます (__call定義されていないメソッドを呼び出したときにのみ呼び出されるため)。しかし、特定のプロパティがあまり頻繁にアクセスされない場合、それがどれほど遅いかは気にしないかもしれません。ポイントは、後で特別な get/set メソッドを追加するのは簡単で、コードの残りの部分はその違いを決して認識しないということです。

私はこのアプローチを Magento から引用しましたが、開発者にとって非常に使いやすいことがわかりました。存在しないプロパティの get/set を呼び出すときに例外をスローすると、タイプミスによる幻のバグを回避するのに役立ちます。プロパティ固有のロジックを独自の get/set メソッドに保持すると、コードの保守が容易になります。ただし、最初にすべてのアクセサー メソッドを記述する必要はありません。他のすべてのコードをリファクタリングすることなく、簡単に戻ってそれらを追加できます。

問題は、何を最適化しようとしているのかということです。開発時間またはコード速度? コード速度を最適化したい場合は、ボトルネックを回避するようにコードを作成する前に、ボトルネックがどこにあるかを確認してください。時期尚早の最適化は諸悪の根源です。

于 2011-06-02T14:52:55.373 に答える
6

これはある種のマイクロ最適化です。理論的には、後で魔法のメソッド (__get および __set) を使用して名前の get/set にロジックを追加できますが、実際にはそれほど必要ありません。繰り返しになりますが、実際には、このパフォーマンスの向上は、他のすべてが最適化されている場合にのみ重要であり、数マイクロ秒でも値が追加されます. この場合、含まれているすべての PHP ファイルを 1 つにマージする、型ヒントを削除する、関数パラメーターの数を減らす、クラスの代わりに単純な関数を使用するなど、他の最適化手法を使用できます。しかし、通常、単純なキャッシングを追加すると、これらすべてのマイクロ最適化よりも 10 ~ 100 倍のパフォーマンス向上が追加されます。

于 2011-06-02T13:56:15.813 に答える
4

ボイラープレートの答えですが、恐れ入りますが、次のことをお勧めします。このプロパティを他のユーザーに公開することで、クラスのカプセル化の問題(ビジネスロジックの適用など)がない場合は、まったく問題ありません。

于 2011-06-02T13:51:04.110 に答える
2

__getおよび__setマジックメソッドを使用することもできます。

class Example
{
    private $allowedProps = array('prop1', 'prop2', 'prop3');
    private $data = array();

    public function __set($propName, $propValue)
    {
        if (in_array($propName, $this->allowedProps))
        {
            $this->data[$propName] = $propValue;
        }
        else
        {
            // error
        }
    }

    public function __get($propName)
    {
        if (array_key_exists($propName, $this->data))
        {
            return $this->data[$propName];
        }
        else
        {
            // error
        }
    }
}
于 2011-06-02T14:02:58.043 に答える
1

最初はびっくりしましたが…wtfのようでした。しかし、脳を数秒巻き込んだ後、この例ではゲッター関数が100万回ループで呼び出されていることに気付きました。もちろん、変数がゲッターでラップされている場合は、命令を追加しました。もちろん、さらに時間がかかります。

私の意見では、ほとんどの状況でこれは非常に些細なことです。なぜなら、実行中にゲッターを100万回呼び出すことに近いイベントが発生するスクリプトにまだ遭遇していないからです。パフォーマンスを最後の一滴まで絞る必要がある場合は、この最適化手法を知っておくとよいでしょう。

于 2011-06-02T14:03:15.553 に答える
0

公開されているかどうかによって異なり$nameます。そうでない場合は、直接アクセス/変更することはできません。トレードオフは、クラスの内部データ要素をインテグレーターに直接公開することです。これは、一部のクラスでは問題ない場合がありますが、他のクラスでは問題ありません。

たとえば、Productクラス内で他の人が製品の価格を直接変更できるようにする必要は必ずしもありません。

于 2011-06-02T13:49:27.513 に答える
0

これは本当に個人的な好みの問題だと思います。パフォーマンスが本当に重要である場合は、あなた自身の質問に答えたと思います.

ただし、最初の例でdog::nameは、2 番目の例と同じように、ゲッター/セッターなしでアクセス$rover->name = 'rover';できます$name

特にクラス メンバーを非表示にする場合は、変数を宣言するprivateprotected、getter/setter が必要になります。

于 2011-06-02T13:54:37.633 に答える