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

发布于 2024-08-05 19:21:39 字数 7912 浏览 2 评论 0原文

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

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

以下是调用代码的示例:

<?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;
    }
}

My first question is basically asking for a code-review. Does the code I'm about to provide use a Factory to promote Polymorphism? Its written in PHP. Here are the basic requirements:

  • Pass a long url to a library and
    return a shortened url. Along with the
    long url, pass user properties to
    attempt to locate the users specific
    shortener service and API key.
  • Allow users to set API keys for specific URL shorteners. My code assumes this is already set in the database and Bitly is the only service supported.
  • If a user doesn't have an API key and service set, use the default API key and service. Again, my code assumes the default is set to Bitly in the database.
  • If the url shortener service fails, log the failure, but don't throw an exception. The library should silently fail. Instead of using the short url, we'll use the long url.

Here is an example of calling the code:

<?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;

Here are the classes:

<?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;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

陪你到最终 2024-08-12 19:21:39

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

$instance = new Object();

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

工厂可以像这样简单:

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

也可以像您喜欢的那样复杂,但这些是要遵循的基本规则。请查看此处的维基百科文章,了解 PHP 示例

The job of the factory pattern is to abstract away the creation of objects. The reason this is useful is because the way in which objects are created may not the same as just:

$instance = new Object();

any time you create it. For example if you first need to deal with loading an include file or you need to pick one of a few derived classes based on some parameter that is not known before runtime.

A factory can be as simple as something like:

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

Or can be as complex as you like, but these are the basic rules to follow. check out the wikipedia article here for a PHP example

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文