Ir para conteúdo

Arquivado

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

bidoofreak

Interpretar diagrama de classes para PHP

Recommended Posts

Oi gente!

 

Recentementeo recebi um modelo de projeto pra ser executado, e nele aparece um diagrama de classes para trabalhar com o PHP orientado a objetos.

 

Até aí tudo bem, posso fazer um CRUD sem problemas orientado a objetos.

 

A questão é que o diagrama que aparece no modelo é muito mais parecido com um diagrama pra Java do que para PHP.

 

Vejam só:

 

Imagem Postada

 

Alguém poderia me ajudar a interpretar como isso deveria ser feito no PHP?

 

Por exemplo, os métodos get() e set() poderiam ser feitos cada um uma só vez, correto?

 

Eu teria que validar os tipos dos dados, por exemplo?

 

Tá meio complicado, ajuda aí gente!

 

Valeu!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Acho ridículo getters and setters crus só pelo fato de ter, só pra dizer que a classe é encapsulada. Mania java podre.

 

se você fizer os getters/setters de maneira genérica vai ter problemas na classe de funcionários, onde não existe a possibilidade de getSenha();

 

PHP é possível trabalhar com as variáveis sem se importar com os tipos de dados, mas se precisar de uma aplicação mais sólida, é recomendável.

 

class cliente {
   private
       $idCliente,
       $txNomeCliente,
       $txEndereco,
       $txTelefone,
       $inDesativado;

   function getId() { return $this->idCliente; }
   function getNomeCliente() { return $this->txNomeCliente; }
   function getEndereco() { return $this->txEndereco; }
   function getTelefone() { return $this->txTelefone; }
   function getInDesativado() { return $this->inDesativado; }

   function setId($id = false) {
       if($id === (int)$id) $this->idCliente = $id;
       else echo 'Id inválido';
   }

   function nomeCliente($nome = false) {
       if($nome === (string)$nome) $this->txNomeCliente = $nome;
       else echo 'Nome inválido';
   }

   function setEndereco($endereco = false) {
       if($endereco === (string)$endereco) $this->txEndereco = $endereco;
       else echo 'Endereço inválido';
   }

...

}

?>

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Evandro Oliveira

 

Eu havia pensado nisso também, e desenvolvi um método de encapsulamento bastante particular a mim.

 

Vê só:

 

class cliente {
    private
        $idCliente;                             // int
        $txNomeCliente,$txEndereco,$txTelefone; // string
        $inDesativado;                          // boolean

    // valida o tipo int
    function getInt($atributo){
        if(is_int($atributo)){
            return $atributo;
        }
    }

     // valida o tipo string
    function getString($atributo){
        if(is_string($atributo)){
            return $atributo;
        }
    }

    // valida o tipo boolean
    function getInt($atributo){
        if(is_bool($atributo)){
            return $atributo;
        }
    }
...
}

O que você acha?

 

E se ficou viável e bem planejado, como eu poderia inserir o set() nessa linha de pensamento?

Compartilhar este post


Link para o post
Compartilhar em outros sites

os getters são ridículos, nem perde tempo com eles, apenas retorna o valor da variável que tem que ser retornada.

os setters que tem que tomar um certo cuidado porque eles só podem permitir o tipo de dados específico da variável.

Compartilhar este post


Link para o post
Compartilhar em outros sites

os getters são ridículos, nem perde tempo com eles, apenas retorna o valor da variável que tem que ser retornada.

os setters que tem que tomar um certo cuidado porque eles só podem permitir o tipo de dados específico da variável.

 

Sim, essa parte eu entendi.

 

A questão é a seguinte: é mais viável passar as variáveis de maneira simples pelo get() e validar seus tipos soment eno set()?

 

e eu teria que fazer um set() pra cada um ou poderia ser como eu mostrei, um set() por tipo.

 

ou então um set() global, mas acho dificil neste caso

 

o que voce acha?

Compartilhar este post


Link para o post
Compartilhar em outros sites

A questão é a seguinte: é mais viável passar as variáveis de maneira simples pelo get() e validar seus tipos soment eno set()?

sim.

 

e eu teria que fazer um set() pra cada um ou poderia ser como eu mostrei, um set() por tipo.

recomendo um para cada, assim pode tratar com cuidado cada campo, e também formatar se, por exemplo futuramente, for necessário converter o telefone em (00) 0000-0000

 

ou então um set() global, mas acho dificil neste caso

Nas duas primeiras classes é possível. Na terceira não.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Maravilha. Até agora tenho este resultado:

 

class Cliente {

	/**
	 * @var Integer
	 */
	private $_idCliente;
	/**
	 * @var String
	 */
	private $_txNomeCliente,$_txEndereco,$_txTelefone;

	/**
	 * @var Boolean
	 */
	private $_inDesativado;

	function getIdCliente()     { return $this->_idCliente; }
	function getTxNomeCliente() { return $this->_txNomeCliente; }
	function getTxEndereco()    { return $this->_txEndereco; }
	function getTxTelefone()    { return $this->_txTelefone; }
	function getInDesativado()  { return $this->_inDesativado; }

	/**
	 * Documentação para as próximas <5> functions
	 * 
	 * -> function set<variavel>($valor)
	 *     -> valida seu tipo
	 *     -> atribui valor
	 * 
	 * @param $variável -> Validação do tipo: Integer,String ou Boolean
	 * @return void
	 */
	function setIdCliente($id = false) {
		if(is_int($id)) $this->_idCliente = $id;
	}
	function setTxNomeCliente($nome = false) {
		if(is_string($nome)) $this->_txNomeCliente = $nome;
	}
	function setTxEndereco($endereco = false) {
		if(is_string($endereco)) $this->_txEndereco = $endereco;
	}
	function setTxTelefone($telefone = false) {
		if(is_string($telefone)) $this->_txTelefone = $telefone;
	}
	function setInDesativado($inDesativado = false) {
		if(is_bool($inDesativado)) $this->_inDesativado = $inDesativado;
	}
}

As validações e formatações todas ficam nesta classe Cliente, que vai manipular os dados recebidos no caso de qualquer manipulação que envolva cliente, certo?

 

No caso do cadastro do cliente, por exemplo, essa classe sozinha já consegue um resultado pra guardar no banco, correto?

 

mas por exemplo, se eu quiser excluir um cliente, e ele já estiver vinculado a uma ordem de serviço, somente a classe Cliente não me resolveria.

 

teriam que ter consultas nas duas classes pra ver os vínculos, certo?

 

ainda tá meio confuso o desenvolvimento desse CRUD.

 

aguardo ajuda, valeu mesmo!

Compartilhar este post


Link para o post
Compartilhar em outros sites

A questão é a seguinte: é mais viável passar as variáveis de maneira simples pelo get() e validar seus tipos soment eno set()?

 

Sim, quem deve validar é o set, imagine que você declarou as propriedades e o construtor as inicializou, a única forma de definir o valor delas é através do set, sendo assim, o get servirá apenas para retornar o valor sendo desnecessário a validação:

 

<?php
/**
* @property integer $idCliente
* @property string $txNomeCliente
* @property string $txEndereco
* @property string $txTelefone
* @property boolean $inDesativado
*/
class Cliente {
/**
 * @var integer
 */
private $idCliente;

/**
 * @var string
 */
private $txNomeCliente;

/**
 * @var string
 */
private $txEndereco;

/**
 * @var string
 */
private $txTelefone;

/**
 * @var boolean
 */
private $inDesativado;

public function __construct(){
	$this->idCliente = 0;
	$this->txNomeCliente = '';
	$this->txEndereco = '';
	$this->txTelefone = '';
	$this->inDesativado = false;
}

public function __get( $name ){
	$ret = null;

	switch ( $name ){
		case 'idCliente':
		case 'txNomeCliente':
		case 'txEndereco':
		case 'txTelefone':
		case 'inDesativado':
			$method = sprintf( 'get%s' , $name );
			$ret = $this->$method();
			break;
		default:
			throw new RuntimeException( sprintf( 'A propriedade %s não existe.' , $name ) );
	}

	return $ret;
}

public function __set( $name , $value ){
	switch ( $name ){
		case 'idCliente':
		case 'txNomeCliente':
		case 'txEndereco':
		case 'txTelefone':
		case 'inDesativado':
			$method = sprintf( 'set%s' , $name );
			$this->$method( $value );
			break;
		default:
			throw new RuntimeException( sprintf( 'A propriedade %s não existe.' , $name ) );
	}
}

public function getIdCliente(){
	return $this->idCliente;
}

public function getTxNomeCliente(){
	return $this->txNomeCliente;
}

public function getTxTelefone(){
	return $this->txTelefone;
}

public function getInDesativado(){
	return $this->inDesativado;
}

public function setIdCliente( $idCliente ){
	if ( is_int( $idCliente ) ){
		$this->idCliente =& $idCliente;
	} else {
		throw new InvalidArgumentException( sprintf( 'A propriedade $idCliente espera um inteiro, %s foi dado.' , gettype( $idCliente ) ) );
	}
}

public function setTxNomeCliente( $txNomeCliente ){
	if ( is_string( $txNomeCliente ) ){
		$this->txNomeCliente =& $txNomeCliente;
	} else {
		throw new InvalidArgumentException( sprintf( 'A propriedade $txNomeCliente espera uma string, %s foi dado.' , gettype( $txNomeCliente ) ) );
	}
}

public function setTxEndereco( $txEndereco ){
	if ( is_string( $txEndereco ) ){
		$this->txEndereco =& $txEndereco;
	} else {
		throw new InvalidArgumentException( sprintf( 'A propriedade $txEndereco espera uma string, %s foi dado.' , gettype( $txEndereco ) ) );
	}
}

public function setTxTelefone( $txTelefone ){
	if ( is_string( $txTelefone ) ){
		$this->txTelefone =& $txTelefone;
	} else {
		throw new InvalidArgumentException( sprintf( 'A propriedade $txTelefone espera uma string, %s foi dado.' , gettype( $txTelefone ) ) );
	}
}

public function setInDesativado( $inDesativado ){
	if ( is_string( $inDesativado ) ){
		$this->inDesativado =& $inDesativado;
	} else {
		throw new InvalidArgumentException( sprintf( 'A propriedade $inDesativado espera um boolean, %s foi dado.' , gettype( $inDesativado ) ) );
	}
}
}

 

Perceba o uso dos métodos mágicos __get e __set:

 

$fulano = new Cliente();
$fulano->idCliente = 10;

 

Se você tentar passar alguma coisa que não for um inteiro:

$fulano = new Cliente();
$fulano->idCliente = 'teste';

 

Você terá um InvalidArgumentException.

 

As classes Cliente e Funcionario estão ok, agora onde está a agregação na OrdemServico ???

No seu diagrama você indicou a agregação, mas nenhuma propriedade armazena um Cliente ou um Funcionario, você até tem os ids, mas na agregação você deveria ter um Cliente e um Funcionario:

 

class OrdemServico {
private $idOrdemServico;
private $funcionario;
private $cliente;
private $dtAbertura;
private $dtFechamento;
private $vlServico;
private $cdStatus;
private $inDesativado;

public function __construct( Funcionario $funcionario , Cliente $cliente ){
	$this->funcionario =& $funcionario;
	$this->cliente =& $cliente;
	//...
}

public function getIdCliente(){
	return $this->cliente->getIdCliente();
}

public function getIdFuncionario(){
	return $this->funcionario->getIdFuncionario();
}
}

 

$cliente = new Cliente();
$cliente->idCliente = 10;

$funcionario = new Funcionario();
$funcionario->idFuncionario = 20;

$orderServico = new OrdemServico( $funcionario , $cliente );
echo $ordemServico->getIdCliente(); //10

Compartilhar este post


Link para o post
Compartilhar em outros sites

@João Batista Neto

 

Entendi perfeitamente, mas é realmente necessário o uso do __get e __set?

 

Afinal, pelo que eu percebi, tudo já está sendo validado com seus devidos métodos após a declaração destes métodos mágicos.

 

Talvez eu esteja enganado.

 

E outra coisa, com relação ao agregamento da ordem de serviço:

 

 

+ getId() : var

+ getIdFuncionario() : var

+ getIdCliente() : var

+ getDataAbertura() : var

+ getDataFechamento() : var

+ getValorServico() : var

+ getStatus() : var

+ getInDesativado() : var

 

+ setId(var) : void

+ setIdFuncionario(var) : void

+ setIdCliente(var) : void

+ setDataAbertura(var) : void

+ setDataFechamento(var) : void

+ setValorServico(var) : void

+ setStatus(var) : void

+ setInDesativado(var) : void

 

Não seriam os

 

+ setIdFuncionario(var) : void

+ setIdCliente(var) : void

 

os responsáveis por fazer isso?

Compartilhar este post


Link para o post
Compartilhar em outros sites

em termos de bancos de dados, se for SQL, o que pode ser feito é o uso de FOREIGN KEYs com o parâmetro

ON DELETE CASCADE

 

assim você apenas apaga o ID do cliente, e todas as OS's vinculadas ao mesmo também vão embora

Compartilhar este post


Link para o post
Compartilhar em outros sites

Entendi perfeitamente, mas é realmente necessário o uso do __get e __set?

 

Absolutamente não, apenas demostrei que é possível utilizar seus getters e setters como se estivesse lidando diretamente com as propriedades.

 

E outra coisa, com relação ao agregamento da ordem de serviço:

 

Não seriam os

 

+ setIdFuncionario(var) : void

+ setIdCliente(var) : void

 

os responsáveis por fazer isso?

 

Seriam, se você tivesse um Cliente e um Funcionario em sua OrdemServico e, segundo seu diagrama você não tem.

 

Imagem Postada

 

E seu PHP ficaria:

class OrdemServico {
private $idOrdemServico;
private $funcionario;
private $cliente;
private $dtAbertura;
private $dtFechamento;
private $vlServico;
private $cdStatus;
private $inDesativado;

public function __construct( Funcionario $funcionario , Cliente $cliente ){
	$this->funcionario =& $funcionario;
	$this->cliente =& $cliente;
	//...
}

public function getIdCliente(){
	return $this->cliente->getId();
}

public function getIdFuncionario(){
	return $this->funcionario->getId();
}
}

 

Perceba que os getters getIdCliente e getIdFuncionario acessam os getters da agregação para retornar os respectivos dados.

Compartilhar este post


Link para o post
Compartilhar em outros sites

@João,

 

entendi perfeitamente.

 

Mas onde eu declararia os métodos da agregação?

 

Na classe de ordem de serviço ou em cada uma?

Compartilhar este post


Link para o post
Compartilhar em outros sites

João, ele tem sim. São a segunda e terceira variáveis da classe da OS

 

Exatamente, foi o que eu quis dizer.

 

Agora embaralhou tudo.

Hehehee

Compartilhar este post


Link para o post
Compartilhar em outros sites

Como é bonito um sistema orientado a objetos, é uma pena que esse conceito não é muito usado em PHP.

 

BTW, isso funciona?

public function __construct( Funcionario $funcionario , Cliente $cliente )

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas onde eu declararia os métodos da agregação?

Na classe de ordem de serviço ou em cada uma?

 

Bom, nesse momento entramos em padrões de projeto, vou mover para a área adequada.

 

Movido:

 

PHP http://forum.imasters.com.br/public/style_emoticons/default/seta.gif Modelagem e Design Patterns

 

Um grande problema de se declarar um Cliente e um Funcionario dentro de OrdemServico é que se amanhã você tiver um novo projeto você terá a OrdemServico conhecendo as classes Cliente e Funcionario e isso não é uma boa prática já que você perde na reutilização.

 

O que você poderia fazer é utilizar um padrão de projeto chamado AbstractFactory, assim sua OrdemServico receberia um objeto que criaria os objetos Funcionario e Cliente:

Imagem Postada

 

Assim:

interface IPessoaFactory {
public function createCliente();
public function createFuncionario();
}

class PessoaFactory implements IPessoaFactory {
public function createCliente(){
	return new Cliente();
}

public function createFuncionario(){
	return new Funcionario();
}
}

class OrdemServico {
private $funcionario;
private $cliente;

public function __construct( IPessoaFactory $factory ){
	$this->funcionario = $factory->createFuncionario();
	$this->cliente = $factory->createCliente();	
}
}

 

Dessa forma, se amanhã você tiver um tipo especial de cliente ou um tipo especial de funcionario, você não precisará modificar sua OrdemServico, basta passar a ela um objeto IPessoaFactory adequado.

Compartilhar este post


Link para o post
Compartilhar em outros sites

São a segunda e terceira variáveis da classe da OS

 

As segunda e terceira variáveis são inteiros, se estamos trabalhando com orientação a objetos, ele deveria ter objetos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

@João Batista Neto

 

Muito obrigado desde já, estou fazendo grande evolução, ou melhor, estamos. :)

 

Agora, seguinte:

 

Mesmo criando os objetos Funcionario e Cliente a partir de um outro objeto recebido, ele terá que ser recebido por algo, certo? Neste momento ele não está me obrigando a conhecer as classes Funcionario e Cliente?

 

Não ficou bem claro sobre esse método AbstractFactory. Seria a criação de uma classe somente pra fazer a mediação entre Funcionario|Cliente e Ordem de Serviços?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mesmo criando os objetos Funcionario e Cliente a partir de um outro objeto recebido, ele terá que ser recebido por algo, certo? Neste momento ele não está me obrigando a conhecer as classes Funcionario e Cliente?

 

Sempre existirão negócios que prestam serviços, quando você constrói uma aplicação de ordem de serviços você deve pensar que possui uma aplicação que deve ser reutilizável para qualquer tipo de serviço. Quando você permite que sua OrdemServico conheça o Funcionário e o Cliente você quebra a abstração uma vez que está lidando diretamente com as implementações de Funcionario e Cliente.

 

Não ficou bem claro sobre esse método AbstractFactory. Seria a criação de uma classe somente pra fazer a mediação entre Funcionario|Cliente e Ordem de Serviços?

 

O uso de AbstractFactory permitirá que sua OrdemServico trabalhe com qualquer tipo de Funcionario ou Cliente, seja o funcionário um técnico de rede que consertará algum problema ou um médico que medicará um paciente. Perceba que tanto o médico quanto o técnico em rede precisarão emitir uma ordem de serviço, é nesse ponto que não se preocupar com a implementação e trabalhar com as interfaces trará a abstração necessária para que você possa reutilizar sua aplicação para ambos.

 

Um outro ponto é a possibilidade de se trocar facilmente os objetos, imagine que sua aplicação utilize hoje um banco de dados baseado em MySQL e amanhã você precise trocar para um Postgree ou então precise exportar os dados de um banco de dados para um XML ou para um outro banco de dados qualquer, se você engessar sua aplicação em um banco ou outro você muito provavelmente terá que reescrever código, utilizando AbstractFactory você apenas precisaria trocar a fábrica do MySQL para uma outra qualquer:

 

Imagem Postada

Dessa forma:

 

<?php
class Order {
public function save( AbstractDatabaseFactory $factory ){
	$stm = $factory->createStatement();
	$stm->prepare( 'INSERT INTO `orders`(`col1`,`col2`) VALUES(?,?);' );

	if ( !$stm->execute( 'valor1' , 'valor2' ) ){
		throw $factory->createException( 'Não foi possível salvar a ordem de serviço' );
	}
}
}

 

Perceba que a classe Order não sabe onde irá salvar os dados, porém ela conhece a interface e isso garantirá que ela saiba como trabalhar com o objeto recebido, dessa forma:

 

$factory = new MySQLDatabaseFactory( 'host=127.0.0.1;dbname=teste' , 'usuario' , 'senha' );

try {
$order = new Order();
$order->save( $factory );
} catch ( AbstractDatabaseException $e ){
echo 'Erro[ ' , $e->getCode() , ' ]: ' , $e->getMessage();
}

 

Ou então:

 

$factory = new XMLDatabaseFactory( 'dados.xml' );

try {
$order = new Order();
$order->save( $factory );
} catch ( AbstractDatabaseException $e ){
echo 'Erro[ ' , $e->getCode() , ' ]: ' , $e->getMessage();
}

 

;)

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

@João Batista Neto

 

Muito interessante este método!

 

Então veja se eu entendi direito ou estou no caminho:

 

Terei essas três classes, Funcionario, Cliente e OrdemDeServico, e mais uma classe Banco.

 

Essas quatro classes darão conta por si só do modelo da aplicação, contendo tudo o que vai ser manipulado pelo CRUD.

 

Na parte do controle é que eu terei mais uma classe, pode ser a classe Fabrica, por exemplo.

 

Essa classe vai fabricar todas as abstracts pra que eu possa trabalhar nas outras quatro. É isso mais ou menos?

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.