Design Patterns

Singleton + Registry – PHP

Em mais um post sobre os patterns, vou falar do Singleton, dando como exemplo o Registry, um dos mais famosos usos do pattern.

O que é?

O Singleton veio com o objetivo de permitir apenas uma única instância de um objeto, sendo o que o método mágico __construct() é privado, impedindo a instanciação. Ele é similar à uma session, meio que uma variável global em forma objeto, sendo que você pode acessá-lo de qualquer parte do projeto.

Obs.: Singleton != Static Method

Representação:

Singleton

Registry:

O Registry é uma forma de aplicação do Singleton bem interessante. Ele serve para guardar dados que serão necessários na aplicação toda, como instâncias de banco de dados, quando se precisa de mais de um (mesmo quando não se precisa, vai que no futuro seja necessário…).

Lembrado que o Registry pode ser aplicado de outras formas, não só com Singleton, é que está é a maneira mais usada.

Implementação: Singleton + Registry

A implementação funcionará da seguinte maneira:

    class Registry{
        /**
         * Conjunto de dados
         * @var instanceof ArrayObject
        */
        private $data;

        /**
         * Aqui ficará a instancia do próprio objeto
         * @var instanceof self
        */
        private static $instance;

        /**
         * Private para não permitir ser instanciado
        */
        private function __construct(){ $this->connections = new ArrayObject; }

        /**
         * Cria a instancia do objeto
         * @return instanceof Registry
        */
        public static function getInstance(){
            if( !( self::$instance instanceof Registry ) ){
                self::$instance = new Registry;
            }

            return self::$instance;
        }

        /**
         * Adiciona um dado ao Registry
         * @param $key Nome para reconhecimento de determinado registro
         * @param $value Registro
         * @throws InvalidArumentException Caso a key já tenha sido adicionada
        */
        public function addElement( $key, $value ){
            if( $this->data->offsetExists( $key ) ){
                throw new InvalidArgumentException( sprintf( '"%s" have already added', $key ) );
            }

            $this->data->offsetSet( $key, $value );
        }

        /**
         * Remove um dado do Registry
         * @param $key O nome relacionado ao registro
         * $throws InvalidArgumentExcpeiton Caso o nome não esteja associado a algum registro
        */
        public function removeElement( $key ){
            if( !$this->data->offsetExists( $key ) ){
                throw new InvalidArgumentException( sprintf( '"%s" not found', $key ) );
            }

            $this->data->offsetUnset( $key );
        }

        /**
         * Retorna um registro
         * @param $key Nome de registro do elemento
         * @return Algum valor registrado
         * $throws InvalidArgumentExcpeiton Caso o nome não esteja associado a algum registro
        */
        public function getElement( $key ){
            if( !$this->data->offsetExists( $key ) ){
                throw new InvalidArgumentException( sprintf( '"%s" not found', $key ) );
            }

            return $this->data->offsetGet( $key );
        }

        /**
         * Retorna se um determinado nome de registro existe
         * @param $key Nome de registro do elemento
         * @return bool
        */
        public function existsElement( $key ){
            return $this->data->offsetExists( $key );
        }
    }

Uso:

Agora você pode, por exemplo, guardar aqui quantas conexões com alguns banco de dados você quiser:

$registry = Registry::getInstance();

$connection1 = new PDO( 'mysql:host=localhost;dbname=db_test', 'user', 'pass' );
$connection1 = new PDO( 'mysql:host=localhost;dbname=db_test2', 'user', 'pass' );

$registry->addElement( 'connection_1', $connection1 );
$retistry->addElement( 'connection_2', $connection2 );

Então em qualquer lugar de sua aplicação você pode fazer isso:

$registry = Registry::getInstance();

$connection = 'connection_1';

if( $registry->existsElement( $connection ) ){
    $pdo = $registry->getElement( 'connection_1' );//Ou connection_2
}

Conclusão:

Como visto, ele ajuda bastante quando se precisa de um objeto que transite por toda aplicação sem ser necessário instanciá-lo toda vez.
O Singleton também é bastante usado por alguns frameworks ao transitar dados relacionados às conexão (não me refiro ao Registry).

Conexão com banco de dados implementando Abstract Factory – PHP

Bom, nessa postagem vou mostrar um exemplo de conexão com o banco de dados implementando o pattern Abstract Factory.

Com esse pattern, é possível fazer alterações no sistema sem precisar de grandes mudanças, apenas, digamos, instanciar um objeto no lugar de outro, tendo uma família de objetos que trabalham de forma transparente. Por exemplo: você tem uma aplicação com banco de dados MySQL, mas você decide trocar de SGBD, para um PostgreSQL. O que fazer? Alterar todo o código de conexão para as necessidades do PostegreSQL? Não! Com esse pattern você só precisa mudar a instância de MySQL para PostgreSQL. Vamos à implementação? Bem, a conexão de cada banco é parecida, então podemos criar uma classe abstrata pra isso:

abstract class AbstractConnectorConfig{
    private $host;
    private $dbname;
    private $user;
    private $password;

    public function __construct( $host, $dbname, $user, $password ){
        $this->host = $host;
        $this->dbname = $dbname;
        $this->user = $user;
        $this->password = $password;
    }

    public function getHost(){
        return $this->host;
    }

    public function getDbname(){
        return $this->dbname;
    }

    public function getUser(){
        return $this->user;
    }

    public function getPassword(){
        return $this->password;
    }

    abstract public function getDsn();
}

Agora para uma configuração de conexão com MySQL:

class MySQLConnectorConfig extends AbstractConnectorConfig{
    public function getDsn(){
        return sprintf( 'mysql:host=%s;dbname=%s',
            $this->getHost(),
            $this->getDbname() );
    }
}

e para PostgreSQL:

class PostgreSQLConnectorConfig extends AbstractConnectorConfig{
    public function getDsn(){
        return sprintf( 'pgsql:host=%s;dbname=%s',
            $this->getHost(),
            $this->getDbname() );
    }
}

Mas para conexão também é necessário um conectador:

interface Connector{
    public function connect( AbstractConnectorConfig $connectorConfig );
    public function disconnect();
    public function isConnected();
    public function getConnection();
}

O principal do PHP é o PDO, então:

class PDOConnector implements Connector{
    private $instance;

    public function __construct( AbstractConnectorConfig $connectorConfig ){
        $this->connect( $connectorConfig );
    }

    public function connect( AbstractConnectorConfig $connectorConfig ){
        if( !$this->isConnected() ){
            $this->instance = new PDO( $connectorConfig->getDsn(),
                $connectorConfig->getUser(),
                $connectorConfig->getPassword() );
            $this->instance->setAttribute( PDO::ATTR_ERRMODE,
                    PDO::ERRMODE_EXCEPTION );
    }
}

    public function disconnect(){
        if( $this->isConnected() ){
            $this->instance = null;
    }
}

    public function isConnected(){
        return ( $this->instance instanceof PDO );
    }

    public function getConnection(){
        return $this->instance;
    }
}

E a implementação fica muito mais simples:

$connector_config = new MySQLConnectorConfig( 'localhost',
                                              'dbname',
                                              'root',
                                              '' );

$connector = new PDOConnector( $connector_config );

e se quiser mudar pra PostgreSQL:

$connector_config = new PostgreSQLConnectorConfig( 'localhost',
                                                   'dbname',
                                                   'root',
                                                   '' );

$connector = new PDOConnector( $connector_config );

Conclusão:

Viram? O código não sofre quase que nenhuma modificação! E o conectador não sabe nem em que banco está se conectando.

Para se aprofundar mais sobre este pattern e muitos outros, aconselho o livro Head First – Design Patterns.

Observer Pattern – PHP

Olá pessoas!

Este é primeira postagem do meu blog e irei falar um pouco sobre o pattern Observer.

Este padrão vai definir uma dependência de um objeto para muitos. “Oh, mas como assim? Exemplifique!”. Ok:

Vamos imaginar uma revista. O que uma revista precisa? De assinaturas! E vocês não concordam que quando essa revista lançar uma nova edição, essa edição será entregue na mesma hora para todos os assinantes (não na mesma hora, mas vai ser enviada na mesma hora)? E que qualquer destes assinantes tem o direito de cancelar essa assinatura a qualquer momento? Então, o pattern Observer é exatamente isso!

Para esse pattern, são necessárias dois participantes: Subject (revista) e Observer (assinante).

interface Subject{
    public function attach( Observer $observer );
    public function dettach( Observer $observer );
    public function notify();
}

attach: Adiciona um observador
remove: Remove um observador
notify: Notifica os observadores

interface Observer{
    public function update( Subject $subject );
}

update: Recebe as atualizações do sujeito

Definidas as interfaces dos nossos participantes, vamos ao código da revista:

class Magazine implements Subject{
    private $subscribers = array();
    private $edition;

    public function __construct( $name ){
        $this->name = $name;
    }

    public function attach( Observer $observer ){
        if( $this->isSubscriber( $observer ) ){
            throw new InvalidArgumentException( 'Subscriber has already added' );
        }

        $this->subscribers[] = $observer;
    }

    public function dettach( Observer $observer ){
        $key = array_search( $observer, $this->subscribers );

        if( $key !== false ){
            unset( $this->subscribers[ $key ] );
        }
    }

    public function notify(){
        foreach( $this->subscribers as $subscriber ){
            $subscriber->update( $this );
        }
    }

    public function newEdition( $edition ){
        $this->edition = $edition;
        $this->notify();
    }

    public function getEdition(){
        return $this->edition;
    }

    public function getName(){
        return $this->name;
    }

    private function isSubscriber( Observer $observer ){
        return in_array( $observer, $this->subscribers );
    }
}

E o código do assinante:

class Subscriber implements Observer{
    private $name;

    public function __construct( $name ){
        $this->name = $name;
    }

    public function update( Subject $subject ){
        $magazine = $subject->getName();
        $edition = $subject->getEdition();

        echo $this->name . ' is reading edition number ' . $edition . ' of the ' . $magazine;
    }
}

Agora vamos simular:

$subscriber1 = new Subscriber( 'Gabriel' );
$subscriber2 = new Subscriber( 'João' );

$magazine = new Magazine( 'Olhe' );
$magazine->attach( $subscriber1 );
$magazine->attach( $subscriber2 );
$magazine->newEdition( '12' );
$magazine->dettach( $subscriber2 );
$magazine->newEdition( '13' );

//12:
//Gabriel is reading edition number 12 of the Olhe
//João is reading edition number 12 of the Olhe
//13:
//Gabriel is reading edition number 13 of the Olhe

Conclusão: com esse padrão, como vimos, é possível compartilhar um determinado recurso com vários participantes de uma vez só, sem se preocupar se vai faltar algum.
Bom, então é isso, nos vemos por ai com mais algum artigo 🙂