5

たとえば、ブログシステム用のORMモデル(PHP Active Record)があります。いいねpostの数を保存するモデルのようなものがあります。はまたは(たとえば)postのいずれかであり、それらは異なるテーブル(したがってモデル)です。picturequote

スキーマは、共有数、いいね、説明などのデータを、またはのpostいずれかとともに保持することです。picturequote

postしたがって、モデルのゲッターを作成するときは、作成する必要があります

public function getX() {
    if ($this->isPicture()) {
       return $this->picture->getX();
    }
    else if ($this->isQuote()) {
       return $this->quote->getX()
    }
    else {
       return self::DEFAULT_X
    }
}

私は現在、多くのゲッターのためにこの構造を書かなければなりません。それを避けるために私にできることはありますか?

PS:それが私のコードなので、PHPとしてタグ付けされています。

編集

  • コメントをコードに変更しました。
  • pictureこれは、aとだけでなく多くのデータを持つモデル(およびDB内の対応するテーブル)ですquote。たとえば、descriptionこれはの一部であり、またはのpostいずれにも存在しません。picturequote
  • picturesとsのテーブルがありquoteます。
  • PHP Active Recordと3つのクラスのそれぞれを使用すると、PHPActiveRecordが提供する汎用モデルクラスが拡張されます。
  • pictureモデルには独自のデータがあります。についても同じですquote
4

2 に答える 2

2

コメントで言及されている戦略パターンのアイデアを拡張するには:

class Post {
    // get the correct 'strategy'
    public function getModel() {
        if ($this->isPicture()) {
            return $this->picture;
        }

        if ($this->isQuote()) {
            return $this->quote;
        }

        return null;
    }

    // using the strategy
    public function getX() {
        $model = $this->getModel();

        if (null === $model) {
            return self::DEFAULT_X;
        }

        return $model->getX();
    }
}

Post各戦略は、おそらく、それらのゲッターを公開するためのクラスと同じインターフェースを実装します。さらに良いのは、(nullを返すのではなく)デフォルトの戦略を提供し、それがデフォルト値を返すようにすることです。そうすれば、各ゲッターのヌルチェックが冗長になります。

于 2012-04-27T00:04:59.997 に答える
0

これに対する代替アプローチは、メタプログラミングの非常に基本的な形式です。アイデアは、手動でメソッドを呼び出すよりも高いレベルに進み、コードにそれを行わせるというものです。

(メソッド定義はすべての一部であると想定しますPost

public function getX($model = null) {
   if ($model) return $model->getX();
   else return self::DEFAULT_X;
}

// usage
$postModel->getX($pictureModel);

ここで起こっていることはgetXPostモデル内のこの単一のインスタンスで、別のクラスの名前を渡し、そのインスタンスで `getX'メソッドを実行していることです(存在し、呼び出し可能である場合)。

これは他の方法で拡張できます。たとえば、メソッドがとにかくそれを行うことができるのに、インスタンスを渡したくない場合があります。

public function getX($model_name = null) {
  if ($model_name && $class_exists($model_name) && is_callable(array($model_name, 'getX')) {
    $model = new $model_name;
    return $model->getX();

  } else {
    return self::DEFAULT_X;
  }
} 

// usage
$postModel->getX('Picture');

この場合、モデルを文字列として渡すと、メソッドが残りを実行します。これにより、必要なものをすばやく取得できますが、常に新しいインスタンスを操作したくない(またはできない)場合があるため、この「便利な」とのトレードオフが少しあります。仕方。

ただし、ゲッターごとに何度も何度も繰り返す必要があるため、それでも問題を完全に解決することはできません。代わりに、次のようなことを試すことができます。

public function __call($method, $args) {
  $class = $args[0];

  if (class_exists($class) && is_callable(array($class, $method))) {
    $model = new $class;  
    return $model->$method();
  }
}

// usage
$postModel->getX('Picture');
$postModel->getY('Quote');
$postModel->getZ('Picture');

モデルに存在しない関数を呼び出すとPost、そのマジックメソッドが呼び出され、引数として指定したモデル名の新しいインスタンスが起動され、getWhateverメソッドが存在する場合はそのメソッドが呼び出されます。 。

他のクラスのメソッドをオーバーライドする場合を除いて、でこれらのゲッターを定義してはならないPostことに注意することが重要です。

ただし、これによって常に新しいインスタンスが作成されるという問題がまだあります。これを修正するには、依存性注入を少し使用できます。つまり、Postクラスに将来使用するクラスの他のインスタンスのリストを含めることができるので、それらを自由に追加および削除できます。

これは私が実際の解決策と考えるものであり、他の例はうまくいけば私がここに到達した方法を示しています(もちろん、物事を明確にするために編集します)。

public $models = array();

public function addModel($instance) {
  $this->models[get_class($instance)] = $instance;
}

public function __call($method, $args) {
  $class = $args[0];

  if (array_key_exists($class, $this->models)) {
    $model = $this->models[$class];
    if (is_callable(array($model, $method)) {
      return $model->$method();
    }
  }     
}

// usage
$this->addModel($pictureModel);
$this->addModel($quoteModel);

$this->getX('Picture');
$this->getY('Quote');

ここでは、モデルの既存のインスタンスをクラスに渡しますPost。クラスは、クラスの名前でキー設定された配列にモデルを格納します。次に、最後の例で説明したようにクラスを使用すると、新しいインスタンスを作成する代わりに、すでに保存されているインスタンスが使用されます。Postこれの利点は、モデルに反映させたいことをインスタンスに対して実行できることです。

これは、プラグインする必要のある新しいモデルを好きなだけ追加できることを意味します。必要なのは、モデルにPostを注入しaddModel、それらのモデルにゲッターを実装することだけです。

それらはすべて、ある時点でどのモデルを呼び出すかをクラスに指示する必要があります。依存モデルの配列があるので、すべてを取得する方法を追加してみませんか?

public function __call($method, $args) {
  $class = $args[0];

  if (array_key_exists($class, $this->models)) {
    $model = $this->models[$class];
    if (is_callable(array($model, $method)) {
      return $model->$method();
    }
  } elseif ($class === 'all') {
    // return an array containing the results of each method call on each model
    return array_map(function($model) use ($method) {
      if (is_callable(array($model, $method) return $model->$method();
    }, $this->models);

  }    
}


// usage
$postModel->getX('all');

getXこれを使用すると、で追加した各モデルの各メソッドの戻り値を含む配列を取得できますaddModel。面倒なロジックを繰り返すことなく、これらすべてを実行する非常に強力な関数とクラスを作成できます。

これらの例はテストされていないことを言及する必要がありますが、少なくとも、あなたができることの概念が明確になっていることを願っています。

注:プロパティへのアクセスに使用されるメソッドにも__GET同じ ことが適用できます。__SETまた、ライブラリがすでにこれらの魔法のメソッドを使用しているというわずかなリスクがあるかもしれないことも言う価値があります。その場合、コードをもう少しインテリジェントにする必要があります。

于 2012-04-26T23:48:47.347 に答える