多级继承替换

Lui*_*igi 8 php oop

我想编写一个模块(特定于框架),它将包装和扩展Facebook PHP-sdk(https://github.com/facebook/php-sdk/).我的问题是 - 如何以一种很好的方式组织课程.

所以详细介绍 - Facebook PHP-sdk包含两个类:

  • BaseFacebook - 带有sdk所有东西的抽象类
  • Facebook - 扩展BaseFacebook,并使用默认会话使用实现父抽象持久性相关方法

现在我有一些功能要添加:

  • Facebook类替换,与框架会话类集成
  • 运行api调用的简写方法,我主要使用(通过BaseFacebook :: api()),
  • 授权方法,所以我不必每次都重写这个逻辑,
  • 配置,从框架类中提取,作为params传递
  • 缓存,与框架缓存模块集成

我知道有些事情已经出错了,因为我有太多的继承,看起来不太正常.将所有内容包装在一个"复杂扩展"类中似乎也太过分了.我想我应该很少工作togheter类 - 但我遇到的问题如下:如果缓存类没有真正扩展并覆盖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

}
Run Code Online (Sandbox Code Playgroud)

Normaly Facebook类扩展了它,并使这些抽象方法变得不可或缺.我用我的替代品替换它 - 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
}
Run Code Online (Sandbox Code Playgroud)

好的,然后我用速记方法和配置变量扩展它:

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

}
Run Code Online (Sandbox Code Playgroud)

我不确定,如果这对单个类来说不是太多(授权/速记方法/配置).然后是另一个扩展层 - 缓存:

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

}
Run Code Online (Sandbox Code Playgroud)

现在这很有效.Facebook_Cache的新实例为我提供了所有功能.Facebook_Custom的速记方法使用缓存,因为Facebook_Cache覆盖了api()方法.但这里有什么困扰我:

  • 我认为这是太多的继承.
  • 这一切都非常紧密 - 就像我必须指定'Facebook_Custom :: api'而不是'parent:api',以避免在Facebook_Cache类扩展上的api()方法循环.
  • 整体混乱和丑陋.

再说一次,这是有效的,但我只是想要以更清洁,更智能的方式进行模式/方式.

cas*_*nca 2

缓存等辅助功能通常作为装饰器实现(我看到您已经在另一条评论中提到过)。装饰器最适合与接口配合使用,因此我首先创建一个:

interface FacebookService {
  public function api();
  public function getUser();
}
Run Code Online (Sandbox Code Playgroud)

保持简单,不要添加任何外部不需要的东西(例如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();
  }
}
Run Code Online (Sandbox Code Playgroud)

现在编写缓存装饰器很容易:

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

进而:

$baseFb = new Facebook_Session();
$fb = new FacebookAdapter($baseFb);
$cachingFb = new CachingFacebookService($fb);
Run Code Online (Sandbox Code Playgroud)

两者$fb都$cachingFb公开相同的FacebookService接口——因此您可以选择是否需要缓存,并且其余代码根本不会改变。

至于你的Facebook_Custom类,现在只是一堆辅助方法;您应该将其分解为一个或多个包装FacebookService并提供特定功能的独立类。一些示例用例:

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

$x = new FacebookBatchWrapper($fb);
$x->api_batch(...);
Run Code Online (Sandbox Code Playgroud)