8

Facebook PHP-sdk ( https://github.com/facebook/php-sdk/ )をラップして拡張するモジュール (フレームワーク固有) を作成したいと考えています。私の問題は、クラスをうまく編成する方法です。

詳細に入ると、Facebook PHP-sdk は 2 つのクラスで構成されます。

  • BaseFacebook - sdk が行うすべてのものを含む抽象クラス
  • Facebook - BaseFacebook を拡張し、デフォルトのセッション使用法で親抽象永続性関連メソッドを実装します

ここで、追加する機能がいくつかあります。

  • フレームワーク セッション クラスと統合された Facebook クラスの置換
  • API 呼び出しを実行する短縮メソッド、私は主に (BaseFacebook::api() を介して) 使用します。
  • 承認方法なので、毎回このロジックを書き直す必要はありません。
  • パラメータとして渡される代わりに、フレームワーククラスから吸い上げられた構成
  • キャッシング、フレームワーク キャッシュ モジュールと統合

非常に正常に見えない継承が多すぎるため、何かが非常に間違っていることはわかっています。すべてを 1 つの「複雑な拡張」クラスにラップすることも、多すぎるように思えます。一緒に動作するクラスはほとんどないはずだと思いますが、次のような問題が発生します。キャッシュクラスが実際に BaseFacebook::api() メソッドを拡張してオーバーライドしない場合、短縮形と認証クラスはキャッシュを使用できません。

たぶん、ある種のパターンがここにあるでしょうか?これらのクラスとその依存関係をどのように整理しますか?

編集 04.07.2012

トピックに関連するコードのビット:

これは、Facebook PHP-sdk の基本クラスの方法です。

abstract class BaseFacebook {

    // ... some methods

    public function api(/* polymorphic */) 
    {
        // ... method, that makes api calls
    }

    public function getUser()
    {
        // ... tries to get user id from session
    }

    // ... other methods

    abstract protected function setPersistentData($key, $value);

    abstract protected function getPersistentData($key, $default = false);

    // ... few more abstract methods

}

通常、Facebook クラスはそれを拡張し、それらの抽象メソッドを実装します。私はそれを私のsubtitudeに置き換えました - Facebook_Session クラス:

class Facebook_Session extends BaseFacebook {

    protected function setPersistentData($key, $value)
    {
        // ... method body
    }

    protected function getPersistentData($key, $default = false)
    {
        // ... method body
    }

    // ... implementation of other abstract functions from BaseFacebook
}

わかりました、次に、省略形のメソッドと構成変数を使用してこれをさらに拡張します。

class Facebook_Custom extends Facebook_Session {

    public function __construct()
    {
        // ... call parent's constructor with parameters from framework config
    }

    public function api_batch()
    {
        // ... a wrapper for parent's api() method
        return $this->api('/?batch=' . json_encode($calls), 'POST');
    }

    public function redirect_to_auth_dialog()
    {
        // method body
    }

    // ... more methods like this, for common queries / authorization

}

これが単一のクラス(承認/短縮メソッド/構成)にとって多すぎないかどうかはわかりません。次に、別の拡張レイヤー - キャッシュがあります。

class Facebook_Cache extends Facebook_Custom {

    public function api()
    {
        $cache_file_identifier = $this->getUser();

        if(/* cache_file_identifier is not null
              and found a valid file with cached query result */)
        {
            // return the result
        }
        else
        {
            try {
                // call Facebook_Custom::api, cache and return the result
            } catch(FacebookApiException $e) {
                // if Access Token is expired force refreshing it
                parent::redirect_to_auth_dialog();
            }
        }

    }

    // .. some other stuff related to caching

}

これでかなりうまくいきます。Facebook_Cache の新しいインスタンスにより、すべての機能が提供されます。Facebook_Cache は api() メソッドを上書きするため、Facebook_Custom の簡易メソッドはキャッシュを使用します。しかし、ここに私を悩ませているものがあります:

  • 継承しすぎだと思います。
  • Facebook_Cache クラスの拡張で api() メソッド ループを回避するために、'parent:api' の代わりに 'Facebook_Custom::api' を指定しなければならなかった方法を見てください。
  • 全体的に雑で醜い。

繰り返しますが、これは機能しますが、これをよりクリーンでスマートな方法で行うパターン/方法について尋ねているだけです。

4

4 に答える 4

2

私はyahoosdkのためにそのようなことをしました、それを入れさせてください、それを試してみてください:)

Facebookがすべてのendメソッド呼び出しに使用しているsdkのクラスであると仮定しましょう。(フレームワークが許す限り)新しいクラスを作成し、そのクラスの変数をFacebookクラスのインスタンスに割り当てることができます。

Facebookのすべてのメソッドに__call()を使用し、カスタムメソッドをラッパークラスに配置します。未定義のすべてのメソッドについては、ラッパーがFacebookクラスに移動し、継承はまったく含まれません。それは私のために働いた。それが役に立てば幸い :)

  Class MyWrapper
    {
       protected $facebook;
       public function __construct()
       {
          $this->facebook = new FaceBook();
       }

       public function __call($method,$args)
       {
          return $this->facebook->$method($args);
       }

       ///define  Your methods //////////

      ///////////////////////////////////
    }

    $t = new MyWrap;
    $t->api(); // Whatever !!!!

編集:

次の複数のクラスに対して複数のラッパーを作成する必要はありません。メソッド呼び出し時に注意する必要があり、ラップされたクラスのインスタンスを保持する変数名に接尾辞を付ける必要があります。

 Class MyWrapper
    {
       protected $facebook;
       protected $facebookCache;
       public function __construct()
       {
          $this->facebook = new FaceBook();
          $this->facebookCache = new FacebookCache();
       }

       public function __call($method,$args)
       {
          $method = explode('_',$method);
          $instance_name = $method[0];
          $method_name = $method[1];
          return $this->$instance_name->$method_name($args);
       }

       ///define  Your methods //////////

      ///////////////////////////////////
    }

    $t = new MyWrap;
    $t->facebook_api(); // Whatever !!!!
    $t->facebookCache_cache();
于 2012-07-12T12:36:42.673 に答える
2

それは確かにあまりにも多くの継承です。ファサードデザインパターンの仕事のように見えます。継承の代わりに構成を使用して、柔軟性を高めます。使用するメソッドを適切なオブジェクトに委任します。

たとえば、基になるクラスのいずれかが変更された場合、変更に適応するようにメソッドを変更するだけで、親メソッドのオーバーライドについて心配する必要はありません。

一般的にそうです、1つのクラスに複数の責任を割り当てることは良い考えではありません。ここで、クラスの責任は外部APIを表すことです。

于 2012-07-10T02:55:03.980 に答える
2

キャッシングなどの補助機能は通常、デコレーターとして実装されます (別のコメントで既に言及されているようです)。デコレーターはインターフェースで最もよく機能するため、まず作成することから始めます。

interface FacebookService {
  public function api();
  public function getUser();
}

シンプルに保ち、外部で必要のないもの ( などsetPersistentData) を追加しないでください。次に、新しいインターフェイスで既存のクラスをラップしBaseFacebookます。

class FacebookAdapter implements FacebookService {
  private $fb;

  function __construct(BaseFacebook $fb) {
    $this->fb = $fb;
  }

  public function api() {
    // retain variable arguments
    return call_user_func_array(array($fb, 'api'), func_get_args());
  }

  public function getUser() {
    return $fb->getUser();
  }
}

これで、キャッシュ デコレータを簡単に作成できます。

class CachingFacebookService implements FacebookService {
  private $fb;

  function __construct(FacebookService $fb) {
    $this->fb = $fb;
  }

  public function api() {
    // put caching logic here and maybe call $fb->api
  }

  public function getUser() {
    return $fb->getUser();
  }
}

その後:

$baseFb = new Facebook_Session();
$fb = new FacebookAdapter($baseFb);
$cachingFb = new CachingFacebookService($fb);

と の両方が同じインターフェイス$fb$cachingFb公開するFacebookServiceため、キャッシュが必要かどうかを選択でき、残りのコードはまったく変更されません。

あなたのFacebook_Customクラスに関して言えば、それは今のところ単なるヘルパー メソッドの集まりです。FacebookService特定の機能をラップして提供する 1 つ以上の独立したクラスに分解する必要があります。ユースケースの例:

$x = new FacebookAuthWrapper($fb);
$x->redirect_to_auth_dialog();

$x = new FacebookBatchWrapper($fb);
$x->api_batch(...);
于 2012-07-15T10:52:49.817 に答える
0

この状況では、リポジトリの設計パターンの方が優れていると思います。私はphpからではありませんが、おっと、それはあなたの問題を解決するはずです..

于 2012-06-30T17:07:06.787 に答える