2

現在、抽象ファクトリを使用して、リクエスト オブジェクトを生成するためのカスタム クラス名を指定できるようにしています。これを行う理由は、コードを変更せずにコア機能を簡単に拡張できるようにするためです。しかし最近、私はこのアプローチの有効性について疑問を持っています. だから私の質問はこれです:

ファクトリが、予想されるインターフェイスに一致する送信されたクラス名をインスタンス化できるようにすることは、ファクトリの概念のろくでなし化ですか? これを避けるために、私はより良いサービスを受けることができますか?

アップデート

ここでの論理は次のとおりです。一方で、実際の自動車工場 (たとえば) は、その種の車を製造するための機械を備えていなければ、自動車を製造することはできません。一方、以下のコードは、同じ自動車工場に、本来意図されていなかったカスタムカーを作るための設計図を与えるようなものです。

別の方法は、ファクトリで使用できるカスタム クラス名を指定する構成オブジェクトを渡し、ファクトリが構成で指定されたカスタム クラス名と明確に一致する場合にのみカスタム クラスを生成するように制限することです。何かご意見は?


そして、関連するコード...

<?php

interface AbstractRequestFactory
{
  public function buildRequest($type);
}

class RequestFactory implements AbstractRequestFactory
{
  public function buildRequest($type='http')
  {
    if ($type == 'http') {
      return new HttpRequest();
    } elseif ($type == 'cli') {
      return new CliRequest();
    } elseif ($custom = $this->makeCustom($type)){
      return $custom;
    } else {
      throw new Exception("Invalid request type: $type");
    }
  }

  protected function makeCustom($type)
  {
    if (class_exists($type, FALSE)) {
      $custom = new $type;
      return $custom instanceof RequestInterface ? $custom : FALSE;
    } else {
      return FALSE;
    }
  }
}

// so using the factory to create a custom request would look like this:

class SpecialRequest implements RequestInterface {}

$factory = new RequestFactory();
$request = $factory->buildRequest('\SpecialRequest');
4

3 に答える 3

1

これはかなり主観的なものですので、以下は一意見にすぎません。

私はこのようなものをすぐに使用することはできません。ファクトリが気にするクラスがほんの一握りしかない場合は、それらをハードコーディングします。しかし、これらのセットが大量にある場合は、適切であると思います。

クラスが適切なインターフェースを拡張していることを検証していることを考えると、フェイルセーフであるため、あなたがしていることに何の問題もないと言えます。そのファクトリ メソッドを使用するコードはきれいに見えます。それが最も重要なことだと思います。

あなたがそのようなテクニックをいたるところで利用しているなら、私はそれに反対します. ただ、これは実装に隠されているので、少し不適切なことをしても余裕があると思います。

于 2012-02-10T00:41:26.517 に答える
1

あなたが持っているものはかなり良さそうです。ファクトリを持つポイントは、いくつかの基準を渡し、メソッドがオブジェクトを返すようにすることです。オブジェクトには、呼び出し元のコードで同じ呼び出し可能なメソッドが使用できると想定されます。RequestInterface を実装することでこの仮定を強制しているため、カスタム要求クラスが同じインターフェイスを実装している限り、「オブジェクト以外で関数を呼び出すことができない」というシナリオに陥ることはありません。

いくつかの推奨事項 (個人的な好み):

  • buildRequest の $type で switch / case を使用します

  • 私はmakeCustom()からnullまたはオブジェクトを返します。そうしないと、戻り値の型(オブジェクトとブール)が混在しています

  • カスタム タイプの数に応じて、混乱を軽減するために、実際にはそれらをスイッチ ケースにハード コードします。誤解しないでほしいのですが、たくさんのクラスを持っていれば素晴らしいのですが、そうでない可能性もあります。

  • 「コードを変更せずにコア機能を簡単に拡張する」部分を、カスタム型クラスによって拡張できる抽象親クラスに入れることを考えたことはありますか?

  • また、ファクトリはオブジェクトを作成するため、静的に設定するのが一般的です。

コード スニペットの例:

public static function getRequest($type='http')
{
    switch ($type) {

        case 'http':
            return new HttpRequest();

        case 'cli':
            return new CliRequest();

        case 'myCustom1':
            return new MyCustom1();

        case 'myCustom2':
            return new MyCustom2();

        default: 
            throw new Exception("Invalid request type: $type");
    }
}

$request = RequestFactory::getRequest($type);

// As long as all objects in factory have access to same methods
$request->doSomething();
$request->andDoSomethingElse();

// Otherwise you end up with that feared 'unable to call function on non-object'
$request->iAmASneakyMethodNotEnforcedByAnInterfaceOrAvailableByExtension();    
于 2012-02-10T00:53:50.343 に答える
1

ディスパッチ配列を使用しないのはなぜですか? すなわち

class RequestFactory 
{
    private static $requests = array(
      'http' => 'HttpRequest',
      'cli' => 'CliRequest',
      'summatelse' => 'Summat'
    );
    public static GetRequest($type)
    {
       if (array_key_exists($type, $requests)) return new $requests[$type];
       else throw new Exception("Invalid request type: $type"); 
    }
}
于 2012-02-10T01:02:06.460 に答える