我想编写一个模块(特定于框架),它将包装和扩展Facebook PHP-sdk(https://github.com/facebook/php-sdk/)。我的问题是 - 如何以一种很好的方式组织课程。
所以深入细节 - Facebook PHP-sdk包含两个类:
现在我有一些功能要添加:
我知道有些事情已经出错了,因为我有太多的继承,看起来不太正常。将所有内容包装在一个“复杂扩展”类中似乎也太过分了。我想我应该很少工作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
}
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
}
好的,然后我用速记方法和配置变量来扩展它:
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_Custom的速记方法使用缓存,因为Facebook_Cache覆盖了api()方法。但这就是困扰我的事情:
所以再一次,这是有效的,但我只是以更清洁,更智能的方式询问模式/方式。
答案 0 :(得分:2)
这确实是太多的遗产。看起来像Facade设计模式的工作。使用组合而不是继承来获得更大的灵活性。将您使用的任何方法委派给适当的对象。
例如,如果任何底层类发生更改,您只需更改方法以适应更改,您就不必担心覆盖任何父方法。
通常是的,将多个职责分配给一个班级并不是一个好主意。在这里,类的职责是表示外部API。
答案 1 :(得分:2)
我为雅虎做过一些事情,让我把它放进去试试吧。)
让我们假设Facebook是用于所有最终方法调用的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();
答案 2 :(得分: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
并提供特定功能的独立类。一些示例用例:
$x = new FacebookAuthWrapper($fb);
$x->redirect_to_auth_dialog();
$x = new FacebookBatchWrapper($fb);
$x->api_batch(...);
答案 3 :(得分:0)
我认为在这种情况下,存储库设计模式会更好。虽然我不是来自PHP但是按照oops它应该解决你的问题..