lundi 9 novembre 2015

Is my front-end controller an example of the 'strategy' design pattern and as written is it unit testable?

I re-wrote an old project and tried to update my spaghetti code with SOLID principles and design patterns. Most of the re-write seemed straight forward, but my front-end controller still looks complicated. I attempted a 'strategy' pattern where the controller instantiates four objects - Interpreter, URLConstructor, Extractor, & Builder - by selecting a specific class for each of the four based on setting in a config file. The code sample is below (sorry if it's long, but I thought I should include the real code.) Is this actually a 'strategy' pattern? As written is this code unit testable? I would like to learn more about dependency injection and unit testing and I worry that this code is a bad example for either.

<?php 

interface iController
{
    public function processRequest(iConfig $config, iHTTPRequest $http_request);
}
?>
<?php 

class ControllerSearch extends aController implements iController
{
    public function __construct(iConfig $config, iHTTPRequest $http_request){
        $this->_results = new Results();
        $this->_process_profiles = $config->getProcessProfiles();
        $this->_resource_packages = new Set();
    }

    public function processRequest(iConfig $config, iHTTPRequest $http_request){
        if ( $http_request->getKEV('query') !== '' ) {
            $this->_createResourcePackages($config, $http_request);
            $this->_retrieveURLs($config);
            $this->_executeDataModeling($config);
            $this->_results->titleSort();
            foreach ( $this->_results as $result ) {
                $this->_setSessionData($result);
            }
        }
        $view = new ViewSearch($config, $http_request);
        $view->compose($this->_results, $http_request);
        $arr = array();
        $arr['logo_src'] = $config->getParam('logo_src');
        $arr['css_default_href'] = $config->getParam('css_default_href');
        $arr['css_custom_href'] = $config->getParam('css_custom_href');
        $arr['resource_packages'] = $this->_resource_packages;
        $arr['http_request'] = $http_request->getArray();
        $arr['model'] = $this->_results; //TODO: remove in prod
        $view->renderTemplate($arr);
    }
}
?>
<?php 

class ControllerAvailability extends aController implements iController
{
    public function __construct(iConfig $config, iHTTPRequest $http_request){
        $this->_results = new Results();
        $this->_process_profiles = $config->getProcessProfiles();
        $this->_resource_packages = new Set();
    }

    public function processRequest(iConfig $config, iHTTPRequest $http_request){
        $this->_createResourcePackages($config, $http_request);
        $this->_retrieveURLs($config);
        $this->_executeDataModeling($config);
        $this->_results->titleSort();
        $arr = array();
        $arr['resource_packages'] = $this->_resource_packages;
        $view = new ViewAvailability($config, $http_request);
        $view->compose($this->_results, $http_request);
        $view->renderTemplate($arr);
    }
}
?>
<?php 

abstract class aController
{
    protected $_results = NULL;
    protected $_process_profiles = NULL;
    protected $_resource_packages = NULL;

    protected function _createResourcePackages(iConfig $config, iHTTPRequest $http_request){
        foreach ( $this->_process_profiles as $process_profile ) {
            if ( $resource_package = $this->_createResourcePackage($config, $process_profile, $http_request) ) {
                $this->_resource_packages->addMember($resource_package);
            }
        }
    }

    protected function _retrieveURLs(iConfig $config){
        $all_content_loaded = TRUE;
        foreach ($this->_resource_packages as $i => $rp) {
            if ( $content = $this->_getCachedData($rp->getURL()) ) {
                $rp->setContent($content);
            } else {
                $all_content_loaded = FALSE;
                trigger_error('Could not retrieve a URL: ' . $rp->getURL(), E_USER_ERROR);
            }
        }

        return $all_content_loaded;
    }

    protected function _executeDataModeling(iConfig $config){
        $modeled_data_added = TRUE;
        foreach ( $this->_resource_packages as $resource_package ) {
            $extractor = $this->_selectExtractor($config, $resource_package->getProcessProfile());
            $builder = $this->_selectBuilder($config, $resource_package->getProcessProfile());
            if ( $extractor !== FALSE && $builder !== FALSE ) {
                $extractor->extract($resource_package->getContent());
                $resource_package->setHits($extractor->getHits());
                $elements = $extractor->getElements();
                foreach ( $elements as $element ) {
                    $builder->buildModel($element);
                    if ( $this->_results->addResult($builder->getModel()) === FALSE ) {
                        $modeled_data_added = FALSE;
                        trigger_error('Could not add model to Results.', E_USER_ERROR);
                    }
                }
            } else {
                $modeled_data_added = FALSE;
                trigger_error('Failed to select Extractor and Builder', E_USER_ERROR);
            }
        }

        return $modeled_data_added;
    }

    private function _createResourcePackage(iConfig $config, iProcessProfile $process_profile, iHTTPRequest $http_request){
        $interpreter = $this->_selectInterpreter($config, $process_profile);
        $url_constructor = $this->_selectURLConstuctor($config, $process_profile);
        if ( $interpreter !== FALSE && $url_constructor !== FALSE ) {
            $interpreter->interpret($config, $process_profile, $http_request);
            $url_constructor->configure($config, $process_profile);
            $url_constructor->setInterpretation($interpreter);
            $url_constructor->evaluateHTTPRequest($http_request);
            $url_constructor->buildURL();
            if ( $rp = ResourcePackage::build($process_profile, $interpreter->getInterpretation(), $url_constructor->getURL()) ) {
                return $rp;
            } else {
                trigger_error('Could not build ResourcePackage. ' . $process_profile->getID() . ' ' . $url_constructor->getURL(), E_USER_ERROR);
            }
        } else {
            trigger_error('Failed to select Interpreter and URLConstructor', E_USER_ERROR);
        }

        return FALSE;
    }

    private function _selectInterpreter($config, $process_profile){
        $name_interpreter = $config->getProcessParam($process_profile, 'interpreter');
        if ( class_exists($name_interpreter) ){
            return new $name_interpreter();
        } else {
            trigger_error('Interpreter class (' . $name_interpreter . ') does not exist for ' . $process_profile->getID(), E_USER_ERROR);
        }

        return FALSE;
    }

    private function _selectURLConstuctor($config, $process_profile){
        $name_url_constructor = $config->getProcessParam($process_profile, 'url_constructor');
        if ( class_exists($name_url_constructor) ){
            return new $name_url_constructor();
        } else {
            trigger_error('URLConstuctor class (' . $name_url_constructor . ') does not exist for ' . $process_profile->getID(), E_USER_ERROR);
        }

        return FALSE;
    }

    private function _selectExtractor($config, $process_profile){
        $name_extractor = $config->getProcessParam($process_profile, 'extractor');
        if ( class_exists($name_extractor) ){
            return new $name_extractor();
        } else {
            trigger_error('Extractor class (' . $name_extractor . ') does not exist for ' . $process_profile->getID(), E_USER_ERROR);
        }

        return FALSE;
    }

    private function _selectBuilder($config, $process_profile){
        $name_builder = $config->getProcessParam($process_profile, 'builder');
        if ( class_exists($name_builder) ){
            return new $name_builder($config, $process_profile);
        } else {
            trigger_error('Builder class (' . $name_builder . ') does not exist for ' . $process_profile->getID(), E_USER_ERROR);
        }

        return FALSE;
    }

    private function _getCachedData($uri){
        $response = FALSE;
        if ( is_string($uri) && $uri !== '' ) {
            $options = array(
                'http' => array('timeout'=>5) //NOTE: timeout in seconds
            );
            $ctx = stream_context_create($options);
            $response = @file_get_contents($uri, NULL, $ctx);
        }
        return $response;
    }

    protected function _setSessionData(iResult $result){
        if ( session_id() !== '' ) {
            if ( $result->getValue('session_key') !== '' ) {
                $_SESSION[$result->getValue('session_key')]['iResult'] = serialize($result);
            } else {
                trigger_error('Could not get value for "session_key."', E_USER_ERROR);
            }
        } else {
            trigger_error('No active PHP session, no session data stored.', E_USER_ERROR);
        }
    }
}
?>

Aucun commentaire:

Enregistrer un commentaire