Ir para conteúdo

Arquivado

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

Ricardo Andrietta

Princípio S.O.L.I.D - OOP

Recommended Posts

Fala pessoal, beleza ?

 

Estou dando uma estudada nos princípios S.O.L.I.D de OOP.

Mais espepecificamente sobre o primeiro ítem ("S"), que trada a responsabilidade única.

 

Como exemplo, estou pensando em um sistema de CRUD.

 

Estou estrutudando da seguinte maneira:


  •  
  • Tenho uma Interface CRUD onde tenho um método chamado "execute()" que deve ser implementado em todas as classes que implementarem esta interface.
  • Estou criando uma classe para cada situação do CRUD, ou seja, uma classe para CREATE, uma para READ, uma para UPDATE e uma para DELETE.

Antigamente eu colocava todos esses métodos na mesma classe.

 

Gostria da opinião de vocês para saber se esta nova forma que descrevi está correta.

Por favor, façam suas considerações.

 

Obrigado :thumbsup:/>

Compartilhar este post


Link para o post
Compartilhar em outros sites

Ao meu ver, você estava fazendo certo já que são rotinas de mesma responsabilidade.

 

Separar uma classe para cada ação talvez devesse ser feito no lado dos Controllers, mas depende muito de como é feito o roteamento da sua Aplicação.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Posta o code? :thumbsup:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Primeiramente obrigado pela atenção de vcs.

 

Eu não postei o código porque gostaria de falar de conceito.

 

Vou especificar um pouco mais a dúvida :blush::

 

Levando em conta o que o Augusto falou, nesta situação eu considero "fazer uma ação no banco" como uma responsabilidade (ai nesse caso eu deixaria os métodos "create()"; "read()"; "update()"; "delete()" na mesma classe);

 

Ou cada um deles tem uma responsabilidade diferente (por exempolo na classe de UPDATE que implementaria a Interface, além do método "execute()" eu poderia colocar métodos que preparam a SQL (não estou falando de validação de dados)

 

tks B)

Compartilhar este post


Link para o post
Compartilhar em outros sites

São classes distintas: a que constói a query e a que executa ela.

 

A classe onde esses quatro métodos estarão implementados utiliza um objeto para, através de seus respectivos métodos, ler, salvar, atualizar e remover informações.

 

Mas ela é totalmente alienada à como cada uma dessas tarefas serão feitas, isto é, do ponto de vista dela não importa se os dados serão armazenados num banco de dados, num XML, num arquivo de texto ou mesmo num servidor remoto.

 

Contanto que o Driver de conexão nela injetado implemente uma interface por ela conhecida, tudo vai funcionar como deve.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bruno, legal esse ponto de vista, não tinha pensado assim, mas você tem razão.

 

Não é de responsabilidade da classe que executa o comando saber onde o dado será guardado, tem que entrar os drivers ai no meio.

 

Obrigado :thumbsup:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então como ficaria o diagrama, Bruno Augusto?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então como ficaria o diagrama, Bruno Augusto?

Ih, rapá. Não tenho a menor idéia.

 

Mas em código, extraído da forma como faço, seria algo assim:

 

interface Driver {

   public function connect();

   public function disconnect();

   public function isConnected();

   public function query( $statement );

   public function prepare( $statement );

   public function lastInsertId( $name = NULL );
}

interface Table {

   public function getTable();

   public function getFields();
}

class Manager {

   private $driver;
   private $table;

   public function __construct( Driver $driver, Table $table ) {

       $this -> driver =& $driver;

       $this -> table =& $table;
   }

   public function select( $columns = '*' ) {}

   public function insert( $name = NULL ) {}

   public function update() {}

   public function delete() {}
}

Essa é a "essência" do meu Table Manager. Ele recebe um Driver de conexão e uma Tabela sobre a qual operar.

 

Todos eles montam as respectivas queries com base nas informações providas pelos métodos da interface Table, os quais toda tabela válida, de entidade ou não, devem implementar.

 

No meu caso, a implementação desses métodos foi tão abstraídas que resultou numa classe base para todas as Tabelas, a AbstractTable:

 

abstract class AbstractTable implements Table {

   public function getTable() {}

   public function getFields() {}
}

Assim toda classe que estender essa será uma Table válida para o Table Manager.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Hum.. interessante. Mas só quero entender o seguinte: Por que tem o Driver, qual seu objetivo? E qual padrão está sendo implementado?

Compartilhar este post


Link para o post
Compartilhar em outros sites

É meu Driver de conexão, não importa qual ou de quê.

 

Por hora tenho eles apenas intermediando os Adapters da PDO, mas como eu citei, posso muito bem ter Drivers que se conectem com um arquivo de texto, um XML, remotamente com uma aplicação REST...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Veja o diagrama com base no exemplo criado pelo Augusto:

 

med_gallery_67291_153_8449.png

 

É no driver que você fará efetivamente o acesso ao local onde vai salvar os dados. Você pode fazer um driver para cada banco de dados que for trabalhar, por exemplo. Um para MySQL, um para SQL Server, um para Oracle e assim por diante.

 

Exatamente por isso o Augusto disse em um de seus posts aqui:

Contanto que o Driver de conexão nela injetado implemente uma interface por ela conhecida, tudo vai funcionar como deve.

 

Ou seja, a classe "Manager" conhece a interface "Driver" que nela está sendo injetado. Afinal, esse é o objetivo de uma interface.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Ricardo Andrietta

Ah, consegui entender agora. Vou pegar este diagrama e trabalhar para codificar, achei bem interessante!

 

Mas pera aí, qual padrão de projeto está sendo implementado? :o

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu acho que nesse pequeno caso, nenhum.

 

Seria apenas uma injeção de dependência.

 

Porém já me disseram que esse Table Manager por si só é um padrão sim, mas eu não tenho certeza.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom, acha que seria interessante pegar este diagrama e fazer tipo uma "junção" com Adapter?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom, acha que seria interessante pegar este diagrama e fazer tipo uma "junção" com Adapter?

 

Não, na maioria das vezes, cada Driver seria um Adapter. O diagrama completo em si não se trata de Adapter, mas cabe.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não, na maioria das vezes, cada Driver seria um Adapter. O diagrama completo em si não se trata de Adapter, mas cabe.

Então não tem o porque integrar junto com o Adapter, se cada Driver já está tratando deste assunto. O que pensei é criar um Driver genérico, que sirva pra "qualquer" tipo de conexão, e então outros Drivers extenderia este Driver, ou faria uma agregação. O quer acha desta idéia? Sugestões?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então não tem o porque integrar junto com o Adapter, se cada Driver já está tratando deste assunto. O que pensei é criar um Driver genérico, que sirva pra "qualquer" tipo de conexão, e então outros Drivers extenderia este Driver, ou faria uma agregação. O quer acha desta idéia? Sugestões?

 

Veja, Driver é uma Interface.

interface Driver {

   public function connect();

   public function disconnect();

   public function isConnected();

   public function query( $statement );

   public function prepare( $statement );

   public function lastInsertId( $name = NULL );
}

 

Cada classe que implemente Driver, é um Adapter em potencial

 


interface FileHandler
{
   function __construct($path, $mode);

   function write($data, $length);

   function read($length);

   function gets($length);

   function getc();

   function tell();

   function dump();

   function rewind();

   ...
}

 

Objetos que implementem FileHandler, obviamente, vão operar no sistema de arquivos

 

Veja o que foi dito:

[...] não importa se os dados serão armazenados num banco de dados, num XML, num arquivo de texto ou mesmo num servidor remoto.

 

Contanto que o Driver de conexão nela injetado implemente uma interface por ela conhecida, tudo vai funcionar como deve.

 

Para tornar esta afirmação verdadeira, precisamos dum Adapter, que converta as operações de Manager em operações de FileHandler

 


class FileHandlerDriver implements FileHandler, Driver
{
   /* A "conexão" seria a abertura do arquivo. Na interface
    * proposta em FileHandler, a abertura do arquivo se dá
    * logo no momento de sua construção.
    */
   public function connect()
   {}

   /* Analogamente à conexão, a desconexão fica por conta do
    * fechamento do arquivo
    */
   public function disconnect()
   {
       $this->__destruct();
   }

   /* A implementação dos métodos connect e disconnect implica
    * que, enquanto existir esta instância, nosso driver estará
    * conectado
    */
   public function isConnected()
   {
       return true;
   }

   /* Dentro de query, vêm algumas checagens sobre que tipo de
    * consulta é realizada. Pela implementação proposta, fica a
    * cargo dos Drivers implementados fazer a conversão das
    * consultas SQL de forma que sejam eficientes.
    * No caso do nosso FileHandler, um exemplo seria verificar
    * se foi recebido um INSERT/UPDATE/DELETE quando nosso
    * arquivo foi aberto como somente leitura.
    */
   public function query($statement)
   { ... }

   /* Como não há explicação de como se utiliza Driver::prepare,
    * não haverá implementação neste exemplo
    */
   public function prepare($statement)
   { ... }

   /* Vamos supor que nos DELETES, nosso arquivo fique com uma
    * linha em branco, desta forma, fica simples contar quantos
    * registros temos nele, basta contar o número de linhas
    */
   public function lastInsertId($name = null)
   {
       return count($this->handler);
   }
}

 

Nesta nossa implementação de exemplo, a principal característica de um adapter está em FileHandlerDriver::disconnect(), que tem por função unicamente apontar para outro método.

 

Esta é a função de um adapter, converter uma interface em outra. Permitir que a interface A seja plenamente funcional através da interface B.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Às vezes eu acho o Adapter é um padrão bem preconceituado.

 

Tem muita gente que segue ele à risca só pra dizer que é um Adapter e quando vê alguma classe nomeada como Adapter, já vai falando besteira.

 

Foi justamente por isso que nomeei como Driver pois apesar de elea ser um adapter (com letra minúsucula) ela não adapta nada, ela apenas fornece implementações concretas de um Driver de conexão, nesse caso, com o DB.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Às vezes eu acho o Adapter é um padrão bem preconceituado.

 

Tem muita gente que segue ele à risca só pra dizer que é um Adapter e quando vê alguma classe nomeada como Adapter, já vai falando besteira.

 

Foi justamente por isso que nomeei como Driver pois apesar de elea ser um adapter (com letra minúsucula) ela não adapta nada, ela apenas fornece implementações concretas de um Driver de conexão, nesse caso, com o DB.

 

O que eu, particularmente, acho uma besteira é nomear as classes de acordo com sua participação no design pattern.

 

Perceba que se eu quiser/precisar passar de MVC para PAC, ou N-tiers eu tenho que sair renomeando minhas classes, provavelmente os nomes de arquivos e ainda sair atrás dos locais onde eles são invocados/instanciados para refatorar????

 

Veja que em momento algum eu chamei o Driver de FileHandlerToDriverAdapter. Veja, também, que eu citei que nem sempre um driver será um adapter. Provavelmente se você está trabalhando só com PDO, sua única adaptação é o connect/disconnect. Eu, já não consideraria um caso de Adapter, e sim Syntactic Sugar.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Cada dia que passa, eu percebo o quanto isso pode ser complexo, em geral do Design Pattern. Eu vejo diagramas, fico estudando, tentando entender, aí quando acho que entendi, não era exatamente aquilo que estava pensando :pinch:

 

Mas é isso aí, gostei da sua explicação.

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.