4

PHP トレイトの目的は、一連のロジックを管理することです。しかし、いくつかの専用プロパティに従って、この一連のロジックを機能させ、名前の競合を回避するための最良の方法は何ですか?

MVC、特にモデル クラスについて考えています。実際、モデルは特性の良い候補のようです。モデルは、ツリー構造を実装したり、ドラフト可能、改訂可能、スラッグ可能などにすることができます。

私はこのようなものを書きたいと思います:

class MyModel extends Model {
    use Tree, Revision, Slug;
    protected $_behaviors = array(
        'Tree' => array('parentFieldname' => 'p_id'),
        'Revision' => array(/* some options */),
        'Slug' => array(/* some options */)
    );
    public function __construct() {
        foreach($this->_behaviors as &$options) {
            $options += /* trait defaults ? */
        }
    }
}

Tree トレイトを次のように設定する場合:

trait Tree {
    protected $_defaults = array(
        'parentFieldname' => 'parent_id',
        /*...other options...*/
    );
    public function moveUp();
    public function moveDown();
    public function setParent(); //<- need the `'parentFieldname' => 'p_id'`attribute
    /*...and so on...*/
}

$_defaults各特性には独自のデフォルトが必要なため、名前の競合が発生します。特性の名前をプロパティ名として使用することは、(new ReflectionClass(__CLASS__))->getTraits())... のようなものを使用することを意味しますが、これは本当に素晴らしいことではありません。

言い換えれば、「オーバーライド可能なデフォルト」で特性を作成し、名前の競合を回避する方法はありますか?

4

4 に答える 4

5

すべての OOP 概念で行うように: 目を開けてください! クラスを拡張し、既存のプロパティを悪用するため、これはまったく同じです。これ以上何もない。

class A {
  protected $defaults = array ('foo' => 'bar');
}
class B extends A {
  protected $defaults = array('foo2' => 42); // Bum: 'foo' disappeared
}

$_defaultsとにかく、ジェネリックプロパティを持つことは、コードの匂いのように聞こえます。「デフォルト」とは何ですか? これはデフォルトですか?システムのデフォルト?アプリケーションのデフォルト? [1] 「デフォルト」ではなく、値を使用してクラスをセットアップします。これは、初期化プロセスまたは具体的なプロパティの初期化 (デフォルト値の拡散) のためのものであるためです( public $parentFieldName = 'parent_id';)

trait A {
  public $parentFieldName = 'parent_id';
  public function construct ($options) { /
     if (isset($options['parentFieldName'])) {
       $this->parentFieldName = $options['parentFieldName'];
     }
  }
}
class Foo {
  use A {
    A::construct as protected constructA;
  }
  public function __construct ($options) {
    $this->constructA($options['A']);
  }
}

いくつかの注意事項:エイリアスを付けることが重要construct()です。これは、(他のトレイトからの) 他のメソッドとも競合する可能性construct()があり、特別なメソッドではないためです。このように(私から)名前が付けられているのは、それが「一種の」コンストラクタであることを明確にするためだけです。もちろん、またはそのような他の名前init()も同様に機能します ;) 「実際の」コンストラクター ( を参照) 内で自分で呼び出す必要がありFoo::__construct()ます。

[1] 識別子としての「デフォルト」は、「タイプ」、「ステータス」、「i」などのようなものです。これらは、役立つように汎用化されています。

于 2013-01-08T14:07:49.543 に答える
3

multiple inheritance特効薬となる手法はいくつかありますが、PHP では実行できません。

複雑なデザイン パターンを省いてシンプルに保つという哲学全体が、このようなことをしようとすると、背中を突き刺します。

はい、Traits導入されました。これは強力なツールになる可能性がありconflict resolutionます。aliasesproperties

ただし、もう 1 つのオプションがありますAspect-Oriented Programming(AOP)。多くの人がそれを理解するのに問題を抱えていますが、私は誰にでもそれに飛び込むことをお勧めします.これは今後数年間で非常に重要になるパラダイムです.

于 2013-06-06T14:21:07.873 に答える
1

追加されたクラスから情報を取得する必要がある場合は、トレイトにinitorメソッドを含めることができます。setOptionsその後、メソッドからこのメソッドを呼び出すことができます__construct

本当の問題は、trait 属性と class 属性の間で競合を起こしてはならないということです。これにより、致命的なエラーが発生します。メソッド名にあるような競合解決はありません。

最も簡単なことは、次のようなものを持つことです。

trait Tree {
    protected $Tree_options; // Prefixing with the trait name should protect you from naming collision (less than optimal)
    public function init($options) {
        // This should be called int he __construct of MyModel
        $this->Tree_options = array_merge($this->Tree_options, $options);
    }
    public function moveUp();
    public function moveDown();
    public function setParent() {
        $parent = $this->Tree_options['p_id'];
    };
}
于 2013-01-08T14:13:33.220 に答える