2012-06-30 39 views
8

Tôi muốn viết một mô-đun (khuôn khổ cụ thể), có thể bao bọc và mở rộng Facebook PHP-sdk (https://github.com/facebook/php-sdk/). Vấn đề của tôi là - cách tổ chức các lớp học, theo một cách tốt đẹp.thay thế thừa kế đa cấp

Vì vậy, đi vào chi tiết - Facebook PHP-sdk bao gồm hai lớp:

  • BaseFacebook - lớp trừu tượng với tất cả các sdk thứ không
  • Facebook - kéo dài BaseFacebook, và thực hiện các mẹ phương pháp kiên trì liên quan đến trừu tượng với việc sử dụng phiên mặc định

Bây giờ tôi có một số chức năng để thêm:

  • Thay thế lớp Facebook, được tích hợp với lớp phiên
  • phương thức viết tắt, chạy các cuộc gọi api, tôi sử dụng chủ yếu Logic mọi thời gian,
  • cấu hình, hút lên từ khuôn khổ lớp học, insted của truyền như params
  • bộ nhớ đệm, tích hợp với mô-đun bộ nhớ cache khuôn khổ

tôi biết một cái gì đó đã đi rất sai, bởi vì tôi có quá nhiều thừa kế trông không bình thường lắm. Bao bọc mọi thứ trong một lớp "mở rộng phức tạp" cũng có vẻ quá nhiều. Tôi nghĩ rằng tôi nên có vài lớp học làm việc togheter - nhưng tôi gặp phải các vấn đề như: nếu lớp cache không thực sự mở rộng và ghi đè phương thức BaseFacebook :: api() - các lớp viết tắt và xác thực sẽ không thể sử dụng bộ nhớ đệm.

Có thể một số loại hình mẫu sẽ đúng ở đây? Làm thế nào bạn sẽ tổ chức các lớp học và phụ thuộc của họ?

EDIT 04.07.2012

Bits mã, liên quan đến chủ đề:

Đây là cách lớp cơ sở của 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 lớp kéo dài nó, và làm suy yếu những phương pháp trừu tượng đó. Tôi thay thế nó với substitude tôi - lớp 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 
} 

Ok, sau đó tôi mở rộng này nhiều hơn với các phương pháp tốc ký và các biến cấu hình:

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 

} 

Tôi không chắc chắn, nếu đây không phải là quá nhiều cho một lớp đơn (ủy quyền/cách viết tắt/cấu hình). Sau đó, có một lớp mở rộng khác - bộ nhớ cache:

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 

} 

Hiện công việc này khá nhiều. Ví dụ mới của Facebook_Cache cho tôi tất cả các chức năng. Viết tắt phương pháp từ Facebook_Custom sử dụng bộ nhớ đệm, bởi vì Facebook_Cache overwrited api() phương pháp.Nhưng đây là những gì đang làm phiền tôi:

  • Tôi nghĩ rằng đó là thừa kế quá nhiều.
  • Đó là tất cả rất chặt chẽ coupled - như nhìn như thế nào tôi đã chỉ định 'Facebook_Custom :: api' thay vì 'cha mẹ: api', để tránh api() phương pháp vòng lặp trên lớp Facebook_Cache mở rộng.
  • Mớ hỗn độn và sự xấu xí chung.

Vì vậy, một lần nữa, điều này có hiệu quả nhưng tôi chỉ hỏi về các mẫu/cách thực hiện điều này một cách gọn gàng và thông minh hơn.

+0

vui lòng cung cấp thêm một số chi tiết để tìm hiểu thêm giải pháp .. –

+0

Tôi đã thêm các bit mã, lỗi mà mất nhiều thời gian. – Luigi

+3

Nhân tiện; đa thừa kế là một lớp mở rộng nhiều hơn một lớp khác. Đó là một cái gì đó hoàn toàn khác. – Sherlock

Trả lời

2

Tính năng phụ như bộ nhớ đệm thường được thực hiện như một trình trang trí (mà tôi thấy bạn đã đề cập trong nhận xét khác). Trang trí làm việc tốt nhất với giao diện, vì vậy tôi sẽ bắt đầu bằng cách tạo một:

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

Giữ nó đơn giản, không thêm bất cứ điều gì bạn không cần bên ngoài (ví dụ như setPersistentData).Sau đó quấn lớp BaseFacebook hiện trong giao diện mới của bạn:

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(); 
    } 
} 

Bây giờ thật dễ dàng để viết một trang trí bộ nhớ đệm:

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(); 
    } 
} 

Và sau đó:

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

Cả $fb$cachingFb lộ cùng một giao diện FacebookService - vì vậy bạn có thể chọn có muốn lưu bộ nhớ cache hay không và phần còn lại của mã sẽ không thay đổi.

Đối với lớp học Facebook_Custom của bạn, nó chỉ là một loạt các phương pháp trợ giúp ngay bây giờ; bạn nên đặt nó vào một hoặc nhiều lớp độc lập quấn FacebookService và cung cấp chức năng cụ thể. Một số trường hợp sử dụng mẫu:

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

$x = new FacebookBatchWrapper($fb); 
$x->api_batch(...); 
+0

toàn diện hơn và được quản lý tốt, cảm ơn bài đăng. –

+0

Điều gì sẽ xảy ra nếu một số Dịch vụ cần nhiều chức năng hơn api() và getUser() từ BaseFacebook? Giao diện cần có nhiều chức năng hơn? Có thể sử dụng _call? Nhưng sau đó có thực sự không cần giao diện, và một dịch vụ có thể nhận được cả hai BaseFacebook hậu duệ hoặc dịch vụ khác ... – Luigi

+0

@Luigi: Bạn có thể thêm chúng vào giao diện nếu bạn cần. Tôi sẽ tránh xa các phương pháp ma thuật như '__call' vì chúng dễ dàng phá vỡ thiết kế OO. – casablanca

0

tôi nghĩ rằng mẫu thiết kế kho lưu trữ sẽ tốt hơn trong tình huống này. mặc dù tôi không phải từ php nhưng theo oops nó sẽ giải quyết vấn đề của bạn ..

2

Nó thực sự là thừa kế quá nhiều. Có vẻ như một công việc cho mẫu thiết kế Facade. Sử dụng bố cục thay vì thừa kế để linh hoạt hơn. Ủy quyền bất kỳ phương thức nào bạn sử dụng cho các đối tượng thích hợp.

Ví dụ: nếu bất kỳ lớp nào bên dưới thay đổi, bạn chỉ có thể thay đổi phương pháp để thích ứng với các thay đổi và bạn sẽ không phải lo lắng về việc ghi đè bất kỳ phương thức mẹ nào.

Nói chung có, bạn không nên gán nhiều trách nhiệm cho một lớp. Ở đây, trách nhiệm của lớp sẽ là đại diện cho một API bên ngoài.

2

Tôi đã thực hiện một số điều như thế cho yahoo sdk hãy để tôi đặt nó, cho nó một thử :)

Cho phép giả định Facebook là lớp trong sdk bạn đang sử dụng cho tất cả các cuộc gọi phương thức kết thúc. Bạn có thể tạo một lớp mới (như công việc khung của bạn cho phép) và gán một biến của lớp cho thể hiện của Lớp Facebook.

Sử dụng __call() cho tất cả các phương pháp của Facebook và đặt các yêu cầu của bạn vào lớp trình bao bọc. cho tất cả các phương thức chưa được xác định, nó bao bọc nó sẽ đi đến lớp Facebook và không có sự thừa kế nào cả. Nó làm việc cho tôi. Hy vọng nó sẽ giúp :)

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 !!!! 

chỉnh sửa:

Bạn không cần phải tạo nhiều wrapper cho nhiều hơn một lớp học sau đây có thể được thực hiện, bạn chỉ cần chăm sóc đồng thời gọi phương thức, có để hậu tố biến thể giữ tên biến của lớp được bao bọc.

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(); 
+1

Tôi đã suy nghĩ về tối nay. Tôi đoán đây được gọi là 'Mẫu trang trí'. Điều này là tốt đẹp, tuy nhiên tôi vẫn còn nghi ngờ về một điều. Tôi cần phải có vài wrappers như thế. Giống như một cho bộ nhớ đệm, một cho phương pháp shorhand, một cho phép. Bây giờ làm thế nào tôi có thể sử dụng nhiều trang trí một cách nhẹ nhàng? Một lớp học riêng biệt để tải một sự kết hợp của họ (mô hình chiến lược/fascade)? Có lẽ bạn có một ý tưởng? – Luigi

+0

Tôi đã chỉnh sửa câu trả lời của tôi, hy vọng nó sẽ giúp bạn kiểm tra :) –

+0

Sau khi chỉnh sửa API của lớp MyWrapper là toàn diện, nhưng bây giờ FacebookCache cả hai không thể sử dụng trực tiếp lớp Facebook và sẽ yêu cầu điều kiện additnioal/hardocding để hợp tác với phương pháp MyWrapper. Tôi thích ví dụ đầu tiên của bạn tốt hơn. Tôi có thể thêm một tham số constructor và sử dụng nó như vậy: '$ fb = new Facebook(); $ fbCache = new FacebookCache ($ fb); $ shorthand = new FacebookShorthand ($ fbCache); $ shorthandNoCache FacebookShorthand ($ fb); 'vv So - khớp nối lỏng lẻo. Lớp Shorhand có thể sử dụng bộ nhớ đệm mà không biết. Tôi sẽ chấp nhận câu trả lời của bạn nếu không có gì tốt hơn xảy ra trước khi thời gian bounty kết thúc :). Cám ơn. – Luigi