我是否使用工厂来促进多态性?

时间:2009-09-23 15:55:30

标签: php oop polymorphism factory-pattern bit.ly

我的第一个问题基本上是要求进行代码审查。我即将提供的代码是否使用工厂来促进多态性?它用PHP编写。以下是基本要求:

  • 将长网址传递给图书馆 返回缩短的网址。随着 long url,将用户属性传递给 尝试找到特定的用户 缩短服务和API密钥。
  • 允许用户为特定的URL缩短程序设置API密钥。我的代码假设已经在数据库中设置了这个,Bitly是唯一支持的服务。
  • 如果用户没有API密钥和服务集,请使用默认的API密钥和服务。同样,我的代码假定默认值在数据库中设置为Bitly。
  • 如果网址缩短服务失败,请记录失败,但不要抛出异常。图书馆应该默默地失败。我们不使用短网址,而是使用长网址。

以下是调用代码的示例:

<?php
$long_url = 'http://www.news.com/story/1';
$account_id = 1;

$use_this_url = $long_url;

$meta = array(
    'account_id' => $account_id,
    // OPTIONS
    // 'service_id' => $service_id,
    // 'account_id' => $account_id,
);
$shortener = new Shortener_Factory($long_url, $meta);
if ($shortener->shorten_long_url() AND $shortener->save_short_url())
{
                $use_this_url = $shortener->short_url;      
}

echo $use_this_url;

以下是课程:

<?php
interface ShortenerServiceInterface {
    public function save_short_url();
    public function shorten_long_url();
}

abstract class ShortenerServiceAbstract implements ShortenerServiceInterface {

    // Url to be shortened
    public $long_url = '';

    // Long url unique id
    public $url_id = 0;

    // Service unique id
    public $shorturlservice_id = 0;

    // Service account unique id
    public $shorturlserviceaccount_id = 0;    

    // Short url service unique API login
    public $api_login = '';

    // Short url service unique API key
    public $api_key = '';

    // Short url service unique hash which maps to original url value
    public $hash = '';

    // Shortened url string
    public $short_url = '';


    // Attempt to call shortner service three times before failing
    public $attempts = 3;

    // Shorten long url with specific service API/logic
    public function shorten_long_url()
    {
        // Can't save a short url when one doesn't exist
        if (!$this->long_url OR !$this->api_login OR !$this->api_key) {
            log('error', 'ShortenerServiceAbstract::shorten_long_url - no long url to shorten - '.var_export($this, TRUE));
            return FALSE;
        }        
    }

    // Save short url and related meta-data to shorturls table
    public function save_short_url()
    {
        // Can't save a short url when one doesn't exist
        if (!$this->url_id OR !$this->hash OR !$this->shorturlservice_id OR !$this->shorturlserviceaccount_id) {
            log('error', 'ShortenerServiceAbstract::save_short_url - no short url to save - '.var_export($this, TRUE));
            return FALSE;
        }

        // Insert a new short url, or update an existing record
        $saved = Shorturl_Model::insert_on_dup_key_update($this->url_id, $this->hash, $this->shorturlservice_id, $this->shorturlserviceaccount_id);

        if (!$saved) {
            log('error', 'ShortenerServiceAbstract::save_short_url - short url record can not be saved - '.var_export($this, TRUE));
            return FALSE;
        } else {
            return TRUE;
        }        
    }

}

// Bitly, a simple url shortener
// @link http://code.google.com/p/bitly-api/wiki/ApiDocumentation
class ShortenerServiceBitly extends ShortenerServiceAbstract {

    public function shorten_long_url()
    {
        // Make sure we have required members set
        parent::shorten_long_url();

        $urlencoded = urlencode($this->long_url);
        $bitlyurl = 'http://api.bit.ly/shorten?version=2.0.1&longUrl='.$urlencoded.'&login='.$this->api_login.'&apiKey='.$this->api_key.'&history=1';

        $attempts = 1;
        while ($attempts <= 3) {
            $json_result = file_get_contents($bitlyurl);        
            if ($json_result) {                                 
                // Return an assoc array
                $json_decode =    json_decode($json_result, TRUE);
                if (is_array($json_decode) AND isset($json_decode['errorCode']) AND $json_decode['errorCode'] == 0) {
                    // Don't compare sent URL with returned URL
                    // Bitly removes invalid poritions of URLs
                    // The camparison might fail even though the URLs are the "same"
                    $shortened = current($json_decode['results']);
                    break;
                } else {
                    log('error', 'ShortenerServiceBitly::shorten_long_url - bit.ly json decoded - '.var_export($json_decode, TRUE));
                }
            } else {
                    log('error', 'ShortenerServiceBitly::shorten_long_url - bit.ly http response - '.var_export($json_result, TRUE));
            }
            $attempts++;
        }

        if (isset($shortened)) {
            $this->short_url = $shortened['shortUrl'];
            $this->hash = $shortened['userHash'];
            return TRUE;
        } else {
            return FALSE;
        }
    }

}

// Shortener Factory
class Shortener_Factory {

    // Shortener service account parameter object
    // @param object shortener account properties
    private $_account;

    // Shortener service object created by factory
    //@param object shorterner service functions
    private $_service; 

    // Url to be shortened
    private $_long_url;

    // Set url members, service parameter object and finally the service itself 
    public function __construct($long_url, $meta=array())
    {
        $this->_long_url = $long_url;

        $this->_set_account($meta);
        $this->_set_service();                
    }

    // Set shortener service account parameter object
    // @param $meta array determine parameters for the current service object
    private function _set_account($meta=array())
    {                    
        $s = FALSE;

        // Get shorturl service account
        if (isset($meta['account_id'])) {
            $s = Shorturlserviceaccount_Model::get_by_account_id($meta['account_id']);
        } elseif (isset($meta['service_id'])) {
            $s = Shorturlserviceaccount_Model::get_by_service_id($meta['service_id']);
        }

        // Account not found, lets use default
        if (!$s) {
            $s = Shorturlserviceaccount_Model::get_default();
        }

        // Must have a service to use
        if ($s === FALSE) {
            log('error', 'Shortener_Core::_set_account - _account not found - '.var_export($this, TRUE));
            return FALSE;
        } else {
            $this->_account = $s;
        }        
    }

    // Use shortener service account parameter object to set shortener service object
    private function _set_service()
    {
        switch ($this->_account->name) {
            case 'bitly':
                $this->_service = new ShortenerServiceBitly;
    break;
            default:
                log('error', 'Shortener_Core::_set_service - _account not set - '.var_export($this, TRUE));
    return FALSE;
        }

        $this->_service->long_url = $this->_long_url;        
        $this->_service->shorturlserviceaccount_id = $this->_account->id;    
        $this->_service->shorturlservice_id = $this->_account->shorturlservice_id;
        $this->_service->api_login = $this->_account->api_login;
        $this->_service->api_key = $this->_account->api_key;
    }

    // Public API for shortener service object methods
    public function __call($name, $arguments)
    {
        if (!$this->_service) {
                log('error', 'Shortener_Core::__call - _service not set - '.var_export($this, TRUE));
    return FALSE;            
        }
        return $this->_service->$name();
    }

    // Public API for shortener service object members
    public function __get($name)
    {
        return ($this->_service->$name) ? $this->_service->$name : NULL;
    }
}

1 个答案:

答案 0 :(得分:0)

工厂模式的工作是抽象出对象的创建。这很有用的原因是因为创建对象的方式可能与以下内容不同:

$instance = new Object();

任何时候你创建它。例如,如果您首先需要处理加载包含文件,或者您需要根据运行时之前未知的某些参数选择一些派生类。

工厂可以像以下一样简单:

function getInstance($objectType, $params)
{
    if (!class_exists($objectType)) {
        throw new Exception('Bad class');
    }
    $instance = new $objectType($params);
    return $instance;
}

或者可以像你喜欢的那样复杂,但这些是遵循的基本规则。查看维基百科文章,了解PHP示例