Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

tocho

Organizar código

Recommended Posts

Antes de começar quero dizer que o foco aqui não é usar framework, nem criar um, essa dúvida também não é para reinventar a roda, apenas, achar a melhor solução para melhorar a organização do código.

 

Eu tenho os seguintes arquivos:

 

class.Mysql.php (responsável pela conexão e persitência)

class.Recados.php (cuida dos recados do site, o nome do arquivo está relacionado ao nome na tabela de banco de dados)

Quando eu desejo inserir um recado eu faço assim:

 

$recados = new Recados();
$recados->insert( array( 'nome'     => $_POST['nome'],
			 'email'    => $_POST['email'],
		));

Na classe recados eu tenho o seguinte:

 

class Recados {
	private $_db;
	
	public function __construct()
	{
		$this->_db = Mysql::getInstance();
	}
	
	public function insert($array)
	{
		 $this->_db->openConnection();
		 
		 return $this->_db->insert("recados", $array);
		 
		 $this->_db->closeConnection();
	}

	public functio delete($id) {}
	public functio update($id) {}
		
}

Se eu tivesse + 10 tabelas (teria mais 10 classes) em todas elas eu teria que repetir a tabela insert, delete, update.

 

Nesse caso, acho que estou falhando na Orientação a Objeto, acredito estar modelando o sistema da forma errada.

 

Queria opiniões e sugestões para melhorar esse código.

Compartilhar este post


Link para o post
Compartilhar em outros sites

O seu exemplo é bastante comum e interessante.

 

Veja bem o que se repetiria: as operações básicas de CRUD. E é por isso que você poderia criar uma BasePersistente que teria essas quatro operações de forma mais genérica e todas as entidades extenderiam essa classe.

 

Deu pra entender?

 

Procure sobre Active Record e o básico de Orientação a Objetos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

O seu exemplo é bastante comum e interessante.

 

Veja bem o que se repetiria: as operações básicas de CRUD. E é por isso que você poderia criar uma BasePersistente que teria essas quatro operações de forma mais genérica e todas as entidades extenderiam essa classe.

 

Deu pra entender?

 

Procure sobre Active Record e o básico de Orientação a Objetos.

 

Isso parece muito intuitivo, acredito que já de conta de grande parte do problema, meu medo também e estar programando de forma "procedural" e achando que estou programando em objetos.

 

Vou ver o que consigo aqui, e posto nesse tópico a evolução ou "desevolução"..rs

 

Obrigado...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Abaixo o código que fiz com sua sugestão, algo bem simples, apenas para entender o conceito.

 

Ainda to com um pouco de dúvida na organização da classe mysql e quais suas responsabilidades, eu sei que usar PDO é muito melhor que MYSQLI, mas em alguns testes que vi lá fora, o MYSQLI foi mais rápido que o PDO.

 

Uma classe MYSQL que faz todo o processo, conecta, desconecta, executa queries, etc..etc.. não fica muito engessada? Eu penso em criar uma paginação, isso seria outro objeto nao é mesmo?.....pois bem, vou estudando aqui, espero que outras idéias possam aflorar por aqui...abs

 


class ActiveRecord
{
	private $_connection = false;
	
	public function __construct()
	{
		// EFETUA A CONEXAO
	}
	
	public static function find()
	{
		echo "achou";
	}
	
	public function select()
	{
		return "resultado";
	}
}

class Recados extends ActiveRecord {}


Recados::find();
Recados::select("WHERE id=10 ORDER BY titulo ASC")


Já melhorou bastante!!!

 

Vamos para um pouco de conceito agora, a classe

Compartilhar este post


Link para o post
Compartilhar em outros sites

A idéia é mais ou menos essa sim.

 

Se você acha que a classe vai ficar engessada, faça a abstração das consultas e crie um objeto pra tratar as transações.

 

Não se preocupe, as coisas vão ficando mais claras a medida que vamos estudando mais.

 

Segue o caminho que tá indo bem!

Compartilhar este post


Link para o post
Compartilhar em outros sites

essa dúvida também não é para reinventar a roda, apenas, achar a melhor solução para melhorar a organização do código.

 

Bom, você está diante de duas situações, DRY e KISS

 

A boa notícia é que, se você enxergou isso, significa que você está começando a ver além.

A má notícia é que isso, invariavelmente, indica falta de planejamento e erro de modelagem.

 

Existem vários problemas no seu código, por exemplo:

 

class Recados {
	private $_db;

	public function __construct(){
		$this->_db = Mysql::getInstance();
	}
}

 

O fragmento acima é totalmente problemático, quase um crime; Quando um objeto conhece outro você fica dependente da implementação.

 

Você pode perguntar: Como assim ?

 

Bom, imagina que amanhã você, por qualquer motivo precise mudar de MySQL para Cassandra ou SQL Server ou Oracle, o que você vai fazer ??? Reescrever a aplicação ?

Ou ainda pior, vai utilizar cometer o maior crime que que um desenvolvedor pode cometer, vai utilizar Ctrl + C, Ctrl + V, criar uma cópia do arquivo, editar a cópia e substituir MySQL::getInstance() para Cassandra::getInstance() ???

 

public function insert($array) {
	$this->_db->openConnection();

	return $this->_db->insert("recados", $array);

	$this->_db->closeConnection();
}

 

No fragmento acima, sua intensão era abrir conexão, inserir os dados, fechar conexão e retornar.

 

Ignorando o fato de que, o seu return interrompe a execução e, consequentemente, o método closeConnection() jamais será executado. Sua intensão era que a conexão fosse fechada e isso é um erro:

 

Onde está a persistência ???

 

Qual o sentido de se utilizar Singleton se você está abrindo e fechando a conexão nas operações de CRUD ???

 

Veja bem, expus os problemas acima de forma dura no sentido de "dar um chaqualhão" e o fiz exatamente porque, se você conseguiu ver que tem um problema é porque você está começando a dar um passo e é sempre melhor dar um passo na direção correta, que ter que dar dois passos para trás amanhã.

 

---------------------------

 

Vamos por partes agora:

 

Problema:

Você armazenará dados em algum lugar, hoje MySQL mas amanhã poderá ser em outro lugar. Para não ficar dependente da implementação e consequentemente ter que reescrever código, seus objetos jamais deverão conhecer o objeto de acesso a dados.

 

 

Solução:

Para que seus objetos não conheçam uns aos outros e, assim, garantir a abstração, você poderá utilizar um padrão de projeto de criação chamado AbstractFactory.

 

AbstractFactory http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Criação

 

Conhecido também como:

Kit

 

Motivação:

Vários bancos de dados possuem formas diferentes para criar, ler, atualizar e excluir os dados, mesmo os bancos que trabalham com SQL, implementam essa sub linguagem de forma diferente, sem contar que as conexões diferem umas das outras. Quando você utiliza, em seus objetos, uma classe específica de banco de dados, você poderá ter um problema no futuro caso seja necessário mudar.

Se, em vez de instanciar uma classe específica, você utilizar uma classe abstrata que define uma interface para a criação dos objetos Connection e Statement, seus objetos jamais ficarão dependentes de uma implementação específica, na verdade, eles jamais saberão qual implementação você estará utilizando já que, de fato, eles estarão trabalhando com a interface de um objeto.

 

Estrutura:

Imagem Postada

 

Participantes:

AbstractFactory (AbstractConnection)

Declara a interface para criação dos produtos abstratos.

 

ConcreteFactory (MySQL, PgSQL)

Implementação de fábrica de produtos concretos.

 

AbstractProduct (AbstractConnector, AbstractStatement)

Declara a interface para os produtos abstratos.

 

ConcreteProduct (MySQLConnector, MySQLStatement, PgSQLConnector, PgSQLStatement)

Implementação dos produtos concretos.

 

Client

É o cara que vai usar tudo isso, sem saber o que estará utilizando.

 

Problema:

Você fará operações de CRUD, essas operações são, muitas vezes diferentes para bancos de dados diferentes. Imagine que você vá paginar algum resultado, ai você modela sua aplicação pensando em MySQL e, amanhã, muda seu banco de dados para SQL Server; Como você modelou sua aplicação totalmente dependente da implementação de paginação em MySQL, você será obrigado a reescrever código para adaptar ao novo banco de dados.

 

Solução:

Para não ficar dependente da sub linguagem SQL e ter problemas com implementações diferentes dela em bancos diferente ou, se você for trabalhar com outro mecanismo de armazenamento que não usa SQL, você deve abstrair o SQL das operações de CRUD. Nesse caso, você pode utilizar um padrão de projeto comportamental chamado Interpreter; Na verdade, não será o Interpreter que você deverá utilizar, mas uma derivação dele, conhecida como Criteria.

 

Interpreter http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Comportamental

 

Intensão:

Definir uma representação da gramática de uma determinada linguagem.

 

Estrutura:

 

Imagem Postada

 

Você também disse:

 

meu medo também e estar programando de forma "procedural" e achando que estou programando em objetos.

 

E logo após veio com:

 

 Recados::find();
 Recados::select("WHERE id=10 ORDER BY titulo ASC")

Perceba que seu código, apesar de estar organizado dentro de uma classe, é totalmente procedural; Cade o objeto ???

 

Bom, aqui vai um exemplo de código para abstração do banco de dados e do SQL:

 

Interfaces:

 

AbstractConnectorConfig.php

<?php
/**
* Interface para configuração de uma conexão com banco de dados
*/
abstract class AbstractConnectorConfig {
	/**
	 * Servidor
	 * @var string
	 */
	private $host;

	/**
	 * Nome do banco
	 * @var string
	 */
	private $name;

	/**
	 * Senha de conexão
	 * @var string
	 */
	private $pswd;

	/**
	 * Usuário de conexão
	 * @var string
	 */
	private $user;

	/**
	 * Constroi um objeto de configuração de conexão
	 * @param string $host Servidor de banco de dados
	 * @param string $name Nome do banco de dados
	 * @param string $user Usuário de conexão
	 * @param string $pswd Senha de conexão
	 */
	public function __construct( $host , $name , $user = null , $pswd = null ){
		$this->host = $host;
		$this->name = $name;
		$this->user = $user;
		$this->pswd = $pswd;
	}

	/**
	 * Recupera o servidor de banco de dados
	 * @return string
	 */
	public function getHost(){
		return $this->host;
	}

	/**
	 * Recupera o nome do banco de dados
	 * @return string
	 */
	public function getName(){
		return $this->name;
	}

	/**
	 * Recupera o usuário de conexão com o banco de dados
	 * @return string
	 */
	public function getUser(){
		return $this->user;
	}

	/**
	 * Recupera a senha de conexão com o banco de dados
	 * @return string
	 */
	public function getPswd(){
		return $this->pswd;
	}

	/**
	 * Recupera a string de conexão com o banco de dados
	 * @return string
	 */
	abstract public function getDSN();
}

 

StatementExpression.php

<?php
/**
* Interface para expressão de uma operação com banco de dados
*/
interface StatementExpression {
	/**
	 * Interpreta a expressão
	 * @param Connector $connector
	 */
	public function interpret( Connector $connector );
}

 

Statement.php

<?php
/**
* Interface para uma operação com banco de dados
*/
interface Statement extends StatementExpression, IteratorAggregate {
	/**
	 * Recupera uma linha do resultado
	 * @return stdClass
	 */
	public function fetch();

	/**
	 * Recupera um Iterator com todas as linhas do resultado
	 * @return Iterator
	 */
	public function fetchAll();

	/**
	 * Define a classe que será utilizada para instanciar o Iterator
	 * retornado por getIterator() e fetchAll()
	 * @param string $class A classe que implementa Iterator
	 * @see IteratorAggregate::getIterator()
	 */
	public function setIteratorClass( $class );
}

 

AbstractStatement.php

<?php
/**
* Base para definição de uma operação com banco de dados
*/
abstract class AbstractStatement implements Statement {
	/**
	 * @var string
	 */
	private $iteratorClass = 'ArrayIterator';

	/**
	 * Recupera uma instância do Iterator
	 * @param array $params Lista de parâmetros que serão passados à instância do Iterator
	 * @return Iterator
	 */
	protected function createIteratorInstance( array $params ){
		$reflection = new ReflectionClass( $this->iteratorClass );

		return $reflection->newInstanceArgs( $params );
	}

	/**
	 * Recupera um Iterator para as linhas do resultado
	 * @return Iterator
	 */
	public function getIterator(){
		$iterator = $this->fetchAll();

		if ( $iterator instanceOf Iterator ){
			return $iterator;
		} else {
			throw new UnexpectedValueException( sprintf( '%s::fetchAll() deve retornar um Iterator.' , get_class( $this ) ) );
		}
	}

	/**
	 * Define a classe que será utilizada para instanciar o Iterator
	 * retornado por getIterator() e fetchAll()
	 * @param string $class A classe que implementa Iterator
	 * @see IteratorAggregate::getIterator()
	 */
	public function setIteratorClass( $class ){
		if ( in_array( 'Iterator' , class_implements( $class , false ) ) ){
			$this->iteratorClass = $class;
		} else {
			throw new LogicException( sprintf( 'A classe %s deve implementar a interface Iterator.' , $class ) );
		}
	}
}

 

Connector.php

<?php
/**
* Interface para definição de um conector com banco de dados
*/
interface Connector {
	/**
	 * Inicia uma transação
	 * @return boolean
	 */
	public function beginTransaction();

	/**
	 * Grava a transação
	 * @return boolean
	 */
	public function commit();

	/**
	 * Executa uma operação no banco de dados
	 * @param Statement $statement
	 * @return boolean
	 */
	public function execute( Statement $statement );

	/**
	 * Recupera o último ID gerado por uma operação de INSERT
	 * @return integer
	 */
	public function lastInsertId();

	/**
	 * Abra a conexão
	 * @param AbstractConnectorConfig $config
	 * @return boolean
	 */
	public function open( AbstractConnectorConfig $config );

	/**
	 * Adiciona aspas em uma string se necessário
	 * @param string $string
	 * @param integer $type O tipo de dado
	 * @return string
	 */
	public function quote( $string , $type );

	/**
	 * Volta as modificações causadas por uma transação
	 * @return boolean
	 */
	public function rollBack();
}

 

ConnectionFactory.php

<?php
/**
* Interface para uma fábrica de objetos relacionados com banco de dados
*/
interface ConnectionFactory {
	/**
	 * Cria um objeto que representa o operador lógico AND
	 * @param StatementExpression $lside Lado esquerdo da operação
	 * @param StatementExpression $rside Lado direito da operação
	 * @return StatementExpression
	 */
	public function createAndExpression( StatementExpression $lside , StatementExpression $rside );

	/**
	 * Cria um objeto que representa uma operação de definição de valor
	 * @param StatementExpression $lside Lado esquerdo da operação
	 * @param StatementExpression $rside Lado direito da operação
	 * @return StatementExpression
	 */
	public function createAssignmentExpression( StatementExpression $lside , StatementExpression $rside );

	/**
	 * Cria um objeto de conexão com banco de dados
	 * @param string $key Chave de identificação da conexão
	 * @param AbstractConnectorConfig $config Dados de configuração da conexão
	 * @return Connector
	 */
	public function createConnector( $key , AbstractConnectorConfig $config );

	/**
	 * Cria uma operação de comparação de igualdade
	 * @param StatementExpression $lside Lado esquerdo da operação
	 * @param StatementExpression $rside Lado direito da operação
	 * @return StatementExpression
	 */
	public function createEqualExpression( StatementExpression $lside , StatementExpression $rside );

	/**
	 * Cria um objeto que representa um campo
	 * @param string $field Nome do campo
	 * @return StatementExpression
	 */
	public function createFieldExpression( $field );

	/**
	 * Cria um objeto que representa uma tabela do banco
	 * @param string $from Nome da tabela
	 * @return StatementExpression
	 */
	public function createFromExpression( $from );

	/**
	 * Cria um objeto que representa um valor numérico
	 * @param float $number
	 * @return StatementExpression
	 */
	public function createNumberExpression( $number );

	/**
	 * Cria um objeto que representa uma string
	 * @param string $string
	 * @return StatementExpression
	 */
	public function createStringExpression( $string );

	/**
	 * Cria um objeto para leitura do banco de dados
	 * @param StatementExpression $field
	 * @param StatementExpression $_
	 * @return StatementExpression
	 */
	public function createReadExpression( StatementExpression $field , StatementExpression $_ );

	/**
	 * Cria objeto que representa uma condição
	 * @param StatementExpression $where
	 * @return StatementExpression
	 */
	public function createWhereExpression( StatementExpression $where );
}

 

 

Implementação MySQL:

 

MySQLConnectorConfig.php

<?php
/**
* Implementação de configuração de conexão para o banco de dados MySQL
*/
class MySQLConnectorConfig extends AbstractConnectorConfig {
	/**
	 * Recupera a string de conexão com o banco de dados
	 * @return string
	 */
	public function getDSN(){
		return sprintf( 'mysql:host=%s;dbname=%s' , $this->getHost() , $this->getName() );
	}
}

 

MySQLStatement.php

<?php
/**
* Interface para definição de uma operação para banco de dados MySQL
*/
abstract class MySQLStatement extends AbstractStatement {
	/**
	 * @var PDOStatement
	 */
	protected $pdoStatement;

	/**
	 * Recupera uma linha do resultado
	 * @return stdClass
	 */
	public function fetch(){
		throw new BadMethodCallException( sprintf( '%s não implementa fetch().' , get_class( $this ) ) );
	}

	/**
	 * Recupera um Iterator com todas as linhas do resultado
	 * @return Iterator
	 */
	public function fetchAll(){
		throw new BadMethodCallException( sprintf( '%s não implementa fetchAll().' , get_class( $this ) ) );
	}

	/**
	 * Define o objeto PDOStatement
	 * @param PDOStatement $statement
	 */
	public function setPDOStatement( PDOStatement $statement ){
		$this->pdoStatement = $statement;
	}

	/**
	 * Verifica se já temos um objeto PDOStatement
	 * @return boolean
	 * @throws RuntimeException Se ainda não tivermos o objeto
	 */
	protected function testPDOStatement(){
		if ( ( $null = is_null( $this->pdoStatement ) ) == true ) throw new RuntimeException( 'A operação ainda não foi executada.' );

		return !$null;
	}
}

 

MySQLFieldExpression.php

<?php
/**
* Implementação para um campo da tabela
*/
class MySQLFieldExpression implements StatementExpression {
	/**
	 * @var string
	 */
	private $name;

	/**
	 * Constroi um novo objeto que representa um campo de uma tabela MySQL
	 * @param string $name Nome do campo da tabela.<p>
	 * Se deixado em branco * será utilizado</p>
	 */
	public function __construct( $name = null ){
		$this->name = empty( $name ) ? '*' : (string) $name;
	}

	/**
	 * Interpreta a expressão
	 * @param Connector $connector
	 */
	public function interpret( Connector $connector ){
		return $this->name == '*' ? '*' : sprintf( '`%s`' , $this->name );
	}
}

 

MySQLFromExpression.php

<?php
/**
* Implementação para uma tabela
*/
class MySQLFromExpression implements StatementExpression {
	/**
	 * @var string
	 */
	private $name;

	/**
	 * Constroi um novo objeto que representa o nome de uma tabela MySQL
	 * @param string $name O nome da tabela
	 */
	public function __construct( $name ){
		$this->name = $name;
	}

	/**
	 * Interpreta a expressão
	 * @param Connector $connector
	 */
	public function interpret( Connector $connector ){
		return sprintf( '`%s`' , $this->name );
	}
}

 

MySQLSelectStatement.php

<?php
/**
* Implementação de uma expressão SELECT para um banco de dados MySQL
*/
class MySQLSelectStatement extends MySQLStatement {
	/**
	 * @var array
	 */
	private $fields;

	/**
	 * @var MySQLFromExpression
	 */
	private $from;

	/**
	 * @var StatementExpression
	 */
	private $where;

	/**
	 * Cria um objeto que representa a instrução SELECT para um banco de dados MySQL
	 * @param MySQLFieldExpression $field Campo que será retornado
	 * @param MySQLFieldExpression $_
	 */
	public function __construct( MySQLFieldExpression $field , MySQLFieldExpression $_ = null ){
		$fields = func_get_args();
		$this->fields = array();

		foreach ( $fields as $field ){
			if ( $field instanceOf MySQLFieldExpression ){
				$this->fields[] = $field;
			} else {
				throw new InvalidArgumentException( 'Os campos devem ser instâncias de MySQLFieldExpression.' );
			}
		}
	}

	/**
	 * Recupera uma linha do resultado
	 * @return stdClass
	 */
	public function fetch(){
		if ( $this->testPDOStatement() ){
			return $this->pdoStatement->fetch( PDO::FETCH_OBJ );
		}
	}

	/**
	 * Recupera um Iterator com todas as linhas do resultado
	 * @return Iterator
	 */
	public function fetchAll(){
		if ( $this->testPDOStatement() ){
			return $this->createIteratorInstance( array( $this->pdoStatement->fetchAll( PDO::FETCH_OBJ ) ) );
		}
	}

	/**
	 * Define a tabela de onde os resultados serão buscados
	 * @param MySQLFromExpression $from
	 */
	public function from( MySQLFromExpression $from ){
		$this->from = $from;
	}

	/**
	 * Interpreta a expressão
	 * @param Connector $connector
	 */
	public function interpret( Connector $connector ){
		$fields = array();

		foreach ( $this->fields as $field ){
			$fields[] = $field->interpret( $connector );
		}

		$select = sprintf( 'SELECT %s FROM %s' , implode( ',' , $fields ) , $this->from->interpret( $connector ) );

		if ( !is_null( $this->where ) ){
			$select .= sprintf( ' %s' , $this->where->interpret( $connector ) );
		}

		return $select;
	}

	/**
	 * Define uma condição para o SELECT
	 * @param WhereExpression $where
	 */
	public function where( WhereExpression $where ){
		$this->where = $where;
	}
}

 

LRExpression.php

<?php
/**
* Base para implementação de uma expressão de dois lados
*/
abstract class LRExpression implements StatementExpression {
	/**
	 * @var StatementExpression
	 */
	protected $lside;

	/**
	 * @var StatementExpression
	 */
	protected $rside;

	/**
	 * Constroi um objeto para um operador AND
	 * @param StatementExpression $lside Lado esquerdo da operação
	 * @param StatementExpression $rside Lado direito da operação
	 */
	public function __construct( StatementExpression $lside , StatementExpression $rside ){
		$this->lside = $lside;
		$this->rside = $rside;
	}
}

 

AndExpression.php

<?php
/**
* Implementação de um operador AND
*/
class AndExpression extends LRExpression {
	/**
	 * Interpreta a expressão
	 * @param Connector $connector
	 */
	public function interpret( Connector $connector ){
		return sprintf( '(%s AND %s)' , $this->lside->interpret( $connector ) , $this->rside->interpret( $connector ) );
	}
}

 

AssignmentExpression.php

<?php
/**
* Implementação de um operador de definição ou igualdade
*/
class AssignmentExpression extends LRExpression {
	/**
	 * Interpreta a expressão
	 * @param Connector $connector
	 */
	public function interpret( Connector $connector ){
		return sprintf( '(%s = %s)' , $this->lside->interpret( $connector ) , $this->rside->interpret( $connector ) );
	}
}

 

WhereExpression.php

<?php
/**
* Implementação de uma condição WHERE
*/
class WhereExpression implements StatementExpression {
	/**
	 * @var StatementExpression
	 */
	private $where;

	/**
	 * Constroi um objeto que representa uma condição
	 * @param StatementExpression $where
	 */
	public function __construct( StatementExpression $where ){
		$this->where = $where;
	}

	/**
	 * Interpreta a expressão
	 * @param Connector $connector
	 */
	public function interpret( Connector $connector ){
		return sprintf( 'WHERE %s' , $this->where->interpret( $connector ) );
	}
}

 

StringExpression.php

<?php
/**
* Implementação de uma string
*/
class StringExpression implements StatementExpression {
	/**
	 * @var string
	 */
	private $string;

	/**
	 * Constroi uma nova string
	 * @param string $string
	 */
	public function __construct( $string ){
		$this->string = (string) $string;
	}

	/**
	 * Interpreta a expressão
	 * @param Connector $connector
	 */
	public function interpret( Connector $connector ){
		return $connector->quote( $this->string , PDO::PARAM_STR );
	}
}

 

NumberExpression.php

<?php
/**
* Implementação de um número
*/
class NumberExpression implements StatementExpression {
	/**
	 * @var float
	 */
	private $number;

	/**
	 * Constroi um novo objeto que representa um número
	 * @param float $number
	 */
	public function __construct( $number ){
		$float = (float) $number;

		if ( (int) $float == (int) $number ){
			$this->number = (int) $number;
		} else {
			$this->number =& $float;
		}
	}

	/**
	 * Interpreta a expressão
	 * @param Connector $connector
	 */
	public function interpret( Connector $connector ){
		return (string) $this->number;
	}
}

 

MySQLConnector.php

<?php
/**
* Implementação de um conector com banco de dados MySQL
*/
class MySQLConnector implements Connector {
	/**
	 * @var PDO
	 */
	private $pdo;

	/**
	 * Inicia uma transação
	 * @return boolean
	 */
	public function beginTransaction(){
		if ( $this->testPDO() ){
			return $this->pdo->beginTransaction();
		}
	}

	/**
	 * Grava a transação
	 * @return boolean
	 */
	public function commit(){
		if ( $this->testPDO() ){
			return $this->pdo->commit();
		}
	}

	/**
	 * Executa uma operação no banco de dados
	 * @param Statement $statement
	 * @return boolean
	 * @throws RuntimeException Se não for possível executar a operação
	 */
	public function execute( Statement $statement ){
		if ( $statement instanceOf MySQLStatement ){
			if ( $this->testPDO() ){
				try {
					$stm = $this->pdo->query( $statement->interpret( $this ) );

					if ( $stm !== false ){
						$statement->setPDOStatement( $stm );
					} else {
						throw new RuntimeException( 'Não foi possível executar a operação.' );
					}
				} catch ( PDOException $e ){
					throw new RuntimeException( 'Não foi possível executar a operação.' , $e->getCode() , $e );
				}
			}
		} else {
			throw new UnexpectedValueException( sprintf( 'Esperamos uma instância de MySQLStatement, %s foi dado.' , get_class( $statement ) ) );
		}

		return true;
	}

	/**
	 * Recupera o último ID gerado por uma operação de INSERT
	 * @return integer
	 */
	public function lastInsertId(){
		if ( $this->testPDO() ){
			return $this->pdo->lastInsertId();
		}
	}

	/**
	 * Abra a conexão
	 * @param AbstractConnectorConfig $config
	 * @return boolean
	 * @throws RuntimeException Se não for possível estabelecer a conexão
	 */
	public function open( AbstractConnectorConfig $config ){
		try {
			$this->pdo = new PDO( $config->getDSN() , $config->getUser() , $config->getPswd() );
			$this->pdo->setAttribute( PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION );
		} catch ( PDOException $e ){
			throw new RuntimeException( 'Não foi possível estabelecer a conexão com o banco de dados.' , $e->getcode() , $e );
		}

		return true;
	}

	/**
	 * Adiciona aspas em uma string se necessário
	 * @param string $string
	 * @param integer $type O tipo de dado
	 * @return string
	 */
	public function quote( $string , $type = null ){
		if ( $this->testPDO() ){
			return $this->pdo->quote( $string , $type );
		}
	}

	/**
	 * Volta as modificações causadas por uma transação
	 * @return boolean
	 */
	public function rollBack(){
		if ( $this->testPDO() ){
			return $this->pdo->rollBack();
		}
	}

	/**
	 * Verifica se a conexão já foi aberta
	 * @return boolean
	 * @throws RuntimeException Se a conexão ainda não tiver sido aberta
	 */
	private function testPDO(){
		if ( ( $null = is_null( $this->pdo ) ) == true ) throw new RuntimeException( 'A conexão ainda não foi aberta.' );

		return !$null;
	}
}

 

MySQL.php

<?php
/**
* Implementação de uma fábrica de objetos relacionados com banco de dados MySQL
*/
class MySQL implements ConnectionFactory {
	/**
	 * Lista de conectores
	 * @var array
	 */
	private $connections = array();

	/**
	 * Cria um objeto que representa o operador lógico AND
	 * @param StatementExpression $lside Lado esquerdo da operação
	 * @param StatementExpression $rside Lado direito da operação
	 * @return StatementExpression
	 */
	public function createAndExpression( StatementExpression $lside , StatementExpression $rside ){
		return new AndExpression( $lside , $rside );
	}

	/**
	 * Cria um objeto que representa uma operação de definição de valor
	 * @param StatementExpression $lside Lado esquerdo da operação
	 * @param StatementExpression $rside Lado direito da operação
	 * @return StatementExpression
	 */
	public function createAssignmentExpression( StatementExpression $lside , StatementExpression $rside ){
		return new AssignmentExpression( $lside , $rside );
	}

	/**
	 * Cria um objeto de configuração de conexão
	 * @param string $host Servidor de banco de dados
	 * @param string $name Nome do banco de dados
	 * @param string $user Usuário de conexão
	 * @param string $pswd Senha de conexão
	 */
	public function createConnectionConfig( $host , $name , $user = null , $pswd = null ){
		return new MySQLConnectorConfig( $host , $name , $user , $pswd );
	}

	/**
	 * Cria um objeto de conexão com banco de dados
	 * @param string $key Chave de identificação da conexão
	 * @param AbstractConnectorConfig $config Dados de configuração da conexão
	 * @return Connector
	 */
	public function createConnector( $key , AbstractConnectorConfig $config ){
		if ( !$this->hasConnector( $key ) ){
			$this->connections[ $key ] = new MySQLConnector();
			$this->connections[ $key ]->open( $config );
		}

		return $this->connections[ $key ];
	}

	/**
	 * Cria uma operação de comparação de igualdade
	 * @param StatementExpression $lside Lado esquerdo da operação
	 * @param StatementExpression $rside Lado direito da operação
	 * @return StatementExpression
	 */
	public function createEqualExpression( StatementExpression $lside , StatementExpression $rside ){
		return new AssignmentExpression( $lside , $rside );
	}

	/**
	 * Cria um objeto que representa um campo
	 * @param string $field Nome do campo
	 * @return StatementExpression
	 */
	public function createFieldExpression( $field ){
		return new MySQLFieldExpression( $field );
	}

	/**
	 * Cria um objeto que representa uma tabela do banco
	 * @param string $from Nome da tabela
	 * @return StatementExpression
	 */
	public function createFromExpression( $from ){
		return new MySQLFromExpression( $from );
	}

	/**
	 * Cria um objeto que representa um valor numérico
	 * @param float $number
	 * @return StatementExpression
	 */
	public function createNumberExpression( $number ){
		return new NumberExpression( $number );
	}

	/**
	 * Cria um objeto que representa uma string
	 * @param string $string
	 * @return StatementExpression
	 */
	public function createStringExpression( $string ){
		return new StringExpression( $string );
	}

	/**
	 * Cria um objeto para leitura do banco de dados
	 * @param StatementExpression $field
	 * @param StatementExpression $_
	 * @return StatementExpression
	 */
	public function createReadExpression( StatementExpression $field , StatementExpression $_ = null ){
		$reflection = new ReflectionClass( 'MySQLSelectStatement' );

		return $reflection->newInstanceArgs( func_get_args() );
	}

	/**
	 * Cria objeto que representa uma condição
	 * @param StatementExpression $where
	 * @return StatementExpression
	 */
	public function createWhereExpression( StatementExpression $where ){
		return new WhereExpression( $where );
	}

	/**
	 * Recupera uma conexão já aberta
	 * @param string $key Chave de identificação da conexão
	 * @return Connector
	 */
	public function getConnection( $key = null ){
		if ( is_null( $key ) && count( $this->connections ) ){
			return current( $this->connections );
		} elseif ( $this->hasConnector( $key ) ){
			return $this->connections[ $key ];
		} else {
			throw new RuntimeException( sprintf( 'Não existe uma conexão para a chave %s.' , $key ) );
		}
	}

	/**
	 * Verifica se uma conexão já existe para a chave informada
	 * @param string $key
	 * @return boolean
	 */
	public function hasConnector( $key ){
		return isset( $this->connections[ $key ] );
	}
}

 

Com isso ai, não importa se você estiver utilizando MySQL, PgSQL, SQL Server, Oracle, Cassandra ou qualquer quer seja o sistema de armazenamento, pode até ser um XML se for o objetivo de sua aplicação, já que todos os objetos foram abstraídos:

 

<?php
$factory = new MySQL(); //Esse cara pode ser mudado para Oracle ou Cassandra que sua aplicação nem saberá o que aconteceu

/**
* Cria o conector
*/
$connector = $factory->createConnector( 'mydb' , $factory->createConnectionConfig( '127.0.0.1' , 'bancodedados' , 'usuario' , 'senha' ) );

/**
* Cria um SELECT usando a fábrica para criar o objeto e os campos que serão utilizados (id e nome)
*/
$statement = $factory->createReadExpression( $factory->createFieldExpression( 'id' ) , $factory->createFieldExpression( 'nome' ) );

/**
* Adiciona ao SELECT a cláusula FROM para a tabela Usuarios
*/
$statement->from( $factory->createFromExpression( 'Users' ) );

/**
* Adiciona ao SELECT algumas condições
*/
$statement->where( $factory->createWhereExpression( $factory->createEqualExpression( $factory->createFieldExpression( 'id' ) , $factory->createNumberExpression( 20 ) ) ) );

/**
* Executa a consulta
*/
if ( $connector->execute( $statement ) ){
	foreach ( $statement as $row ){
		var_dump( $row );
	}
}

 

Agora, tudo o que você precisará é passar a fábrica para seus objetos ORM:

 

<?php
abstract class TableDataGateway {
	protected $factory;

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

	protected function read( array $fields = null ) {
		if ( !is_null( $fields ) ) {
			foreach ( $fields as $key => $field ) {
				$fields[ $key ] = $this->factory->createFieldExpression( $field );
			}

			$statement = call_user_func_array( array( $this->factory , 'createReadExpression' ) , $fields );
		} else {
			$statement = $this->factory->createReadExpression( $this->factory->createFieldExpression( '*' ) );
		}

		$statement->from( $this->factory->createFromExpression( get_class( $this ) ) );

		return $statement;
	}
}

class Users extends TableDataGateway {
	public function getUserList() {
		$stm = $this->read();

		$this->factory->getConnection()->execute( $stm );

		return $stm;
	}
}

 

E para usar:

<?php
$factory = new MySQL();
$factory->createConnector( null , $factory->createConnectionConfig( '127.0.0.1' , 'seubancodedados' , 'usuario' , 'senha' ) );

$users = new Users( $factory );

foreach ( $users->getUserList() as $user ) {
	var_dump( $user );
}

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Sempre utilizei design paterns em java, agora em php, achei que seria complicado, mas até é bem parecido a java, estou alterando um sistema em php que rola legal, alguns modulos que funcionam com o Totvs e acho que vou utilizar algo assim, pow o joão entrou na briga dando vuadora... q show!

Compartilhar este post


Link para o post
Compartilhar em outros sites

espero que o criador do tópico tenha entendido alguma coisa.

 

Sim, todos esperamos.

 

Mas a beleza do fórum é que, se ele não tiver compreendido, ele poderá perguntar sempre, até que todos os conceitos tenham sido absorvidos e ele possa resolver o problema dele da melhor forma possível.

 

:D

Compartilhar este post


Link para o post
Compartilhar em outros sites

 

espero que o criador do tópico tenha entendido alguma coisa.

 

Sim, todos esperamos.

 

Mas a beleza do fórum é que, se ele não tiver compreendido, ele poderá perguntar sempre, até que todos os conceitos tenham sido absorvidos e ele possa resolver o problema dele da melhor forma possível.

 

:D

 

Assim você nos deixa orgulhosos João!

 

É muito bom ter a garantia de poder chegar no fórum com uma dúvida conceitual totalmente paradigmática e ter alguém com que possamos discutir sobre.

 

Continua assim, brother!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Uma verdadeira aula sem pagar nada por isso, perfeito!!!!!!!

 

Demorei para responder pois estava viajando, achei a explicação do João fantástica, puder olhar rapidamente agora e depois vou fazer tudo com calma, quero montar essa estrutura novamente para entender bem os conceitos e fazê-la funcionar em 2 bancos diferentes, postarei aqui todo o progresso dúvidas e idéias.

 

 

A boa notícia é que, se você enxergou isso, significa que você está começando a ver além.

A má notícia é que isso, invariavelmente, indica falta de planejamento e erro de modelagem.

 

 

A boa notícia é que eu precisava ver realmente que estava pensando tudo errado, e dessa forma não dá neh...

 

Como gratidão ao sua explicação impecável, estarei estudando isso a fundo e postarei dúvidas futuras...

 

Obrigado..e avante!!!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Depois da maravilhosa explicação do João resolvi começar os estudos.

 

Primeiro queria entender bem o conceito de Abstract Factory e dar uma reciclada, resolvi fazer algo pensando no mundo real e cheguei no código abaixo:

 

abstract class FabricaChocolate
{
	abstract public function coberturaChocolate();
}

class Nestle extends FabricaChocolate
{
	public function coberturaChocolate()
	{
		print("Cobertura de chocolate Nestle");
	}
}

class Garoto extends FabricaChocolate
{
	public function coberturaChocolate()
	{
		print("Cobertura de chocolate Garoto");
	}
}

class SelecionaFabrica 
{
	public function recebeFabrica(FabricaChocolate $f)
	{
		return $f->coberturaChocolate();
	}
}

$nestle = new Nestle();
$garoto = new Garoto();

$fabrica = new SelecionaFabrica();
$fabrica->recebeFabrica($nestle);
$fabrica->recebeFabrica($garoto);

 

Se algum conceito estiver errado, fiquem à vontade para corrigirem.

 

Obrigado e avante...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Se algum conceito estiver errado, fiquem à vontade para corrigirem.

 

Ok amigo:

 

AbstractFactory - creational pattern http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Fábrica Abstrata - padrão de criação.

 

Fabricar, verbo transitivo direto http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Quem fabrica, fabrica alguma coisa.

 

Veja bem, seu código ilustra a situação de uma fábrica de chocolates mas, cadê o chocolate ???? Você não fabricou nada.

 

Trazendo seu código para a vida real, seria o mesmo de você construir uma fábrica de chocolates, ter lá dentro um funcionário e, quando alguém bater à porta da fábrica, o funcionário lá dentro gritará a todo pulmão:

 

-- "Cobertura de chocolate Nestlé"

 

Mas, cadê a cobertura ??? Você não fabricou nada !!! Cade o produto ???

 

Quando eu apresentei AbstractFactory eu disse que se tratava de um design pattern de criação, disse também que seus participantes eram:

 

- AbstractFactory http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Define a interface da fábrica

- ConcreteFactory http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Implementa a interface da fábrica

- AbstractProduct http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Define a interface do produto

- ConcreteProduct http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Implementa a interface do produto

 

No seu código, você trata apenas da AbstractFactory e ConcreteFactory, ou seja, você andou apenas metade do caminho.

 

Imagina que você depare-se com uma pessoa, de 1.90 metros de altura, dentro de um buraco com 20 metros de profundidade.

 

Você precisa ajudar a pessoa a sair do buraco e, para isso, joga-lhe uma corda de 10 metros de comprimento.

 

Subtraindo da profundidade do buraco, a altura da pessoa, existirá 18.1 metros. Subtraindo desses 18 metros resultantes, o comprimento da corda, ainda existirá 8 metros de diferença entre a ponta da corda e a pessoa.

 

Ou seja, você até teve a intenção de ajudar mas, como sua corda compreende apenas metade do caminho, o problema persiste.

 

tem esse artigo tambem bem bacana q traz isso tambem do "mundo real"

 

O artigo apontado pelo Will é totalmente adequado.

 

Quando você ilustrou, no seu código, uma fábrica de chocolate, você tratou apenas da interface da fábrica.

 

Então, para implementar AbstractFactory, você precisa primeiro compreender que você precisará ter, necessariamente, 2 interfaces:

 

AbstractFactory - interface para a fábrica de produtos.

AbstractProduct - interface para os produtos que serão fabricados.

 

Então, a primeira coisa a se fazer para compreender AbstractFactory é compreender a interface de um objeto:

 

http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Métodos de interface e Polimorfismo - Parte1

http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Métodos de interface e Polimorfismo - Parte2

http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Métodos de interface e Polimorfismo - Parte3

 

 

Compreendido os conceitos de interface (não deixe de ler todo o curso PHP Orientado a Objetos), você precisará estar consciente que um padrão de projeto visa solucionar problemas de projeto.

 

AbstractFactory define uma interface para a criação de uma família de produtos relacionados ou dependente uns dos outros sem que você precise especificar explicitamente as classes.

 

No seu código você tem apenas 1 produto, não existem dependências ou relacionamentos. Nesse caso, você deveria utilizar FactoryMethod.

 

FactoryMethod http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Criação

Também conhecido como:

Virtual Constructor (construtor virtual)

 

Motivação:

Muitas vezes, ao desenvolver uma aplicação, deparamos com situações onde um objeto à ser instanciado é específico a situação e muitas vezes, as situações são tão diversas que não é possível prever qual objeto deverá ser instanciado. FactoryMethod oferece a solução no sentido que, ele retarda e abstrai a criação do objeto.

 

Aplicabilidade:

Você pode utilizar FactoryMethod quando uma classe não pode prever ou antecipar a criação de seus objetos.

 

Estrutura:

http://dp.improjetos.com.br/FactoryMethod.png

Participantes:

Product (chocolate)

Define a interface do produto que a fábrica criará.

 

ConcreteProduct (chocolate nestlé)

Implementação da interface Product.

 

Creator (FabricaDeChocolate)

Declara o método fábrica que retornará um Product, a própria fábrica poderá chamar o método fábrica para criar um Product.

 

ConcreteCreator (Nestlé)

Implementação do método fábrica que retornará o ConcreteProduct

 

Exemplo de código:

 

Product.php

<?php
/**
* Interface para definição de um produto que a fábrica criará
*/
interface Product {
/**
 * Recupera o nome do produto
 * @return string
 */
public function getName();

/**
 * Recupera o preço do produto
 * @return float
 */
public function getPrice();
}

 

ChocolateNestle.php

<?php
/**
* Implementação da interface Product para um Chocolate da Nestlé
*/
class ChocolateNestle implements Product {
/**
 * Recupera o nome do produto
 * @return string
 */
public function getName(){
	return 'Chocolate Nestlé';
}

/**
 * Recupera o preço do produto
 * @return float
 */
public function getPrice(){
	return 3.3;
}
}

 

ChocolateGaroto.php

<?php
/**
* Implementação da interface Product para um Chocolate da Garoto
*/
class ChocolateGaroto implements Product {
/**
 * Recupera o nome do produto
 * @return string
 */
public function getName(){
	return 'Chocolate Garoto';
}

/**
 * Recupera o preço do produto
 * @return float
 */
public function getPrice(){
	return 3;
}
}

 

Creator.php

<?php
/**
* Base para criação de uma fábrica de chocolates
*/
abstract class Creator {
/**
 * Vende um produto
 * @return Product
 */
public function vende(){
	$product = $this->factoryMethod();

	echo 'Vendendo ' , $product->getName() , ' por R$ ' , $product->getPrice() , PHP_EOL;

	return $product;
}

/**
 * Método fábrica que deverá ser definido pelo ConcreteCreator
 * @return Product
 */
abstract public function factoryMethod();
}

 

Nestle.php

<?php
/**
* ConcreteCreator que fabricará chocolates Nestlé
*/
class Nestle extends Creator {
/**
 * Implementação do método fábrica que retornará produtos do tipo ChocolateNestle
 * @return Product
 */
public function factoryMethod(){
	return new ChocolateNestle();
}
}

 

Garoto.php

<?php
/**
* ConcreteCreator que fabricará chocolates Nestlé
*/
class Garoto extends Creator {
/**
 * Implementação do método fábrica que retornará produtos do tipo ChocolateGaroto
 * @return Product
 */
public function factoryMethod(){
	return new ChocolateGaroto();
}
}

 

Cliente.php

<?php
/**
* O cliente que comprará o chocolate de alguém
*/
class Cliente {
/**
 * Quem compra, compra alguma coisa de alguém, nesse caso, poderemos comprar
 * qualquer coisa de qualquer um que implemente Creator
 * @param Creator $creator
 */
public function compra( Creator $creator ){
	$product = $creator->vende();

	echo 'Compramos ' , $product->getName();
}
}

 

Veja só:

 

$factory = new Nestle();

$cliente = new Cliente();
$cliente->compra( $factory );

 

No fragmento acima, instanciamos a fábrica Nestlé e passamos esse objeto ao Cliente, a saída será:

Vendendo Chocolate Nestlé por R$ 3.3

Compramos Chocolate Nestlé

 

Mas, se modificarmos a fábrica:

$factory = new Garoto();

$cliente = new Cliente();
$cliente->compra( $factory );

 

A saída será:

Vendendo Chocolate Garoto por R$ 3

Compramos Chocolate Garoto

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Muito bom em João.. curti a sua explicação... boa iniciativa ;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.