Ir para conteúdo

POWERED BY:

Arquivado

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

RSS iMasters

PHPog: uma questão de design?

Recommended Posts

Primeiramente, gostaria de agradecer ao André Noel pela gentileza em permitir o uso da tirinha abaixo nesse artigo:

40004.png

Quando falamos sobre design patterns, estamos falando sobre soluções reutilizáveis para problemas recorrentes. Utilizamos essas soluções para evitar repetições e redundâncias no código da aplicação (DRY  => Don't repeat yourself) e, assim, ter um código consistente, escalável, legível e fácil de manter. Na prática, escrevemos códigos para que sejam soluções para problemas e não para criar novos problemas.

Já faz algum tempo que o PHP tem tido funcionalidades adicionadas à parte de POO (programação orientada a objetos) que, sinceramente, não possuem nada de POO. De fato, estão mais para Pog (Programação Orientada a Gambiarras) do que POO. Vejamos um exemplo:

$o = new SomeClass();

Como eu sei que você é um programador que gosta de orientação a objetos, lhe entrego a instância acima e digo para você trabalhar com ela.

O que você fará com isso ?

- A não ser que o desenvolvedor que escreveu SomeClass esteja ao seu lado ou que você tenha poderes mágicos, você não saberá como trabalhar com isso, e não saberá pelo simples fato de que você não conhece a interface desse objeto.

A programação orientada a objetos baseia-se no comportamento das coisas e saber usar esse comportamento se dá, exclusivamente, através da interface dessas coisas.

Se você estiver no escuro sem absolutamente nenhuma fonte de luz e eu colocar alguma coisa em sua mão e disser: "produza algo com essa coisa?, o que você fará?

Como você não vê, você não saberá o que fazer com essa coisa.

Agora, imagine que eu lhe diga:

interface Something {

public function doSomething();

}

 

class SomeClass implements Something {

public function doSomething() {

echo 'POO é legal';

}

}

Agora que você sabe que SomeClass é, na verdade, Something, você já sabe como trabalhar com aquela instância inicial:

$o = new SomeClass();

$o->doSomething();

De um tempo para cá, temos visto grandes mudanças no PHP, muitas delas interessantes e realmente úteis. Mas muitas delas, apesar de ditas como ?OOP?, acabam batendo de frente com orientação a objetos.

É o caso de muitos métodos mágicos como __get, __set, __call e __callStatic.

class SomeMagicThing {

public function __call( $name , array $argv ) {

switch ( $name ) {

case 'fly' :

echo 'flying';

break;

case 'run' :

echo 'running';

break;

default:

echo 'opz...';

}

}

}

 

$o = new SomeMagicThing();

Se você não ler o código, você não vai saber que esse objeto pode voar, ou seja, esses ditos métodos mágicos não são realmente mágicos, na verdade são feitos para que programadores que possuem poderes mágicos ou uma bola de cristal possam utilizar.

Mas, quando saímos dessas ?coisas? e vemos adições no core do PHP como funções anônimas (ou lambdas), traits e outras, devemos ficar realmente preocupados. Não por ser mais uma coisa não OOP, mas pelo fato de o PHP incentivar a falta de design.

Sim, design é tudo e, quando vemos a própria linguagem incentivando um ?código largado?, devemos ficar seriamente preocupados.

Utilizamos funções anônimas em códigos procedurais, nos quais precisamos utilizar callbacks para notificar que alguma coisa aconteceu ou que está acontecendo e alguma precisa ocorrer paralelamente.

Callback não é coisa de código orientado a objetos, consequentemente, funções anônimas não têm espaço em um código OOP.

Existem dois design patterns que podemos utilizar para não utilizar funções anônimas:

  1. Observer (GoF - Behavioral), caso um objeto dependa do estado de outro objeto.
  2. Command (GoF - Behavioral), caso uma requisição deva ser encapsulada em um objeto.

O primeiro design pattern, Observer:

40006.png

interface Observer {

public function update( Subject $s );

}

 

interface Subject {

public function attach( Observer $o );

public function detach( Observer $o );

public function notify();

}

 

class SomeClass implements Subject {

private $observers = array();

private $myState = 10;

 

public function attach( Observer $o ) {

$this->observers[] = $o;

}

 

/**

* Muda o estado do objeto

*/

public function changeState() {

++$this->myState;

$this->notify();

}

 

public function detach( Observer $o ) {

foreach ( $this->observers as $offset => $observer ) {

if ( $observer == $o ) {

unset( $this->observers[ $offset ] );

}

}

}

 

public function getState() {

return $this->myState;

}

 

public function notify() {

foreach ( $this->observers as $observer ) {

$observer->update( $this );

}

}

}

 

class SomeOtherClass implements Observer {

/**

* @var SomeClass

*/

private $someClass;

 

public function __construct( SomeClass $someClass ) {

$this->register( $someClass );

}

 

private function register( Subject $someClass ) {

$this->someClass = $someClass;

$this->someClass->attach( $this );

}

 

public function update( Subject $subject ) {

echo 'O estado do objeto observado mudou, o novo estado é:';

echo $this->someClass->getState();

}

}

 

$subject = new SomeClass();

$observer = new SomeOtherClass( $subject );

 

$subject->changeState();

O outro design pattern, Command, pode ser chamado como substituição orientada a objetos para callbacks. Mas vai muito além de ser apenas um ?oop callback?, tanto é que também é conhecido como Transaction.

40008.png

interface Command {

public function execute();

}

 

class ConcreteCommand implements Command {

/**

* @var Receiver;

*/

private $receiver;

 

public function __construct( Receiver $receiver ) {

$this->receiver = $receiver;

}

 

public function execute() {

$this->receiver->action();

}

}

 

class Receiver {

public function action() {

echo 'Ação executada';

}

}

 

class Invoker {

public function doSomething( Command $callback ) {

echo 'Fazendo alguma coisa....';

 

$callback->execute();

}

}

 

$r = new Receiver();

$i = new Invoker();

$c = new ConcreteCommand( $r );

 

$i->doSomething( $c );

Claro que o exemplo acima é meramente ilustrativo, mas poderíamos ter toda uma estrutura de histórico e rollBack com os comandos executados.

Mas a coisa começa a ficar realmente feia quando nos deparamos com uma nova adição do PHP 5.4, os traits.

Lembra da tirinha lá do início?

Pois bem, todos concordamos que RCP não é nada elegante, mas e quanto aos traits? O que esperar de um recurso cuja descrição do próprio autor é: ?It is almost like a language supported and failsafe copy'n'paste mechanism to build classes".

Se não acredita, veja o RFC no wiki do php.net: https://wiki.php.net/rfc/traits

Agora, só porque RCP é suportado pelo core da linguagem, copy'n'paste ficou mais elegante?

Em orientação a objetos, fazer é um verbo bitransitivo: fazemos alguma coisa com alguma outra coisa.

Com isso em mente, podemos utilizar um outro design pattern para adicionar responsabilidades dinamicamente a um objeto e ter um meio flexível à herança para estender funcionalidade:

40010.png

interface Component {

public function saySomething();

}

 

class Hello implements Component {

public function saySomething() {

$this->sayHello();

}

 

public function sayHello() {

echo 'Hello ';

}

}

 

class World implements Component {

public function saySomething() {

$this->sayWorld();

}

 

public function sayWorld() {

echo 'World';

}

}

 

class Decorator implements Component {

private $hello;

private $world;

 

public function __construct( Hello $hello , World $world ) {

$this->hello = $hello;

$this->world = $world;

}

 

public function sayExclamationMark() {

echo '!';

}

 

public function sayHello() {

$this->hello->sayHello();

}

 

public function sayWorld() {

$this->world->sayWorld();

}

 

public function saySomething() {

$this->sayHello();

$this->sayWorld();

$this->sayExclamationMark();

}

}

 

class DecoratorB extends Decorator {

private $component;

 

public function __construct( Component $component ) {

$this->component = $component;

}

 

public function saySomething() {

$this->component->saySomething();

$this->sayPogIsBad();

}

 

public function sayPogIsBad() {

echo PHP_EOL , 'POG is Bad';

 

$this->sayExclamationMark();

}

}

 

$decorator = new DecoratorB( new Decorator( new Hello() , new World() ) );

$decorator->saySomething();

Bom, para finalizar, gostaria de deixar uma pergunta:

PHPog é uma questão de design ou a linguagem está apenas se adaptando à falta de design nos códigos PHP escritos por POGramadores?

 

http://imasters.com.br/artigo/21865/php/phpog-uma-questao-de-design

Compartilhar este post


Link para o post
Compartilhar em outros sites

Os scripts em PHP são "pogueadas" devido a sua própria natureza.

O próprio criador do PHP, Rasmus Lerdorf, é um POGramador e ainda defende a causa.

 

 

Programadores OOP são construtores de linguagens e os POGramadores são modeladores de negócios.

 

 

São duas classes distintas de programadores que deveriam se completar ao invés de se gladiarem.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu queria saber como o Commando pode ser aplicado onde uma Closure entraria em, sei lá, array_map(), por exemplo.

 

Vale toooooda essa volta apenas para criação uma função-zinha requerida pela função especificada?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Concordo com o Bruno Augusto nesse ponto.

Pra que criar uma classe inteira pra uma funçãozinha de b05t@ de 2 linhas.

Todo recurso adicionado a uma linguagem pode ser útil SE BEM UTILIZADO, apesar de achar os métodos mágicos, com exceção do __toString(), a coisa mais porca já inventada.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Depende do contexto também né, Henrique.

 

Tipo, a classe-pai dos meus Controllers e a minha View usam fortemente os métodos mágicos.

 

Sem eles, os templates ficariam ainda mais feios do que já são:

 

<h1><?php echo $this -> getVar( 'title' ); ?></h1>

Ou:

 

<h1><?php echo $this -> title; ?></h1>

Qual fica mais intuitivo?

 

O problema é que tem gente que abusa, né...

Compartilhar este post


Link para o post
Compartilhar em outros sites
Qual fica mais intuitivo?

Na minha opinião, a primeira, afinal é exatamente o que você está fazendo: 'pegando' uma variável...

 

Métodos mágicos são lentos. Imagina uma 'conversinha' entre o interpretador PHP e essa classe aí:

 

Com método mágico:

PHP:

- E aí objeto, beleza cara?

Objeto:

- Beleza, interpretador, e você?

PHP:

- Tô ótimo. Escuta, me mandaram pegar um atributo seu aí, o nome é 'title'

Objeto:

- Putz, cara, eu até tenho um atributo desses, mas ele é privado, só eu posso acessá-lo.

PHP:

- Mas você não implementa aquele tal método mágico?

Objeto:

- Pior que é verdade, deixa eu achar esse atributo aqui então... Pronto, tá aí...

 

Sem método mágico:

PHP:

- E aí objeto, beleza cara?

Objeto:

- Beleza, interpretador, e você?

PHP:

- Tô ótimo. Escuta, me mandaram executar o método getVar e passar 'title' como argumento. Me retorna essa variável aí.

Objeto:

- É pra já!

 

Concordo que é menos verboso, até poderia se justificar uma pequena perda em performance, mas eu reluto a utilizar esse tipo de recurso.

É birra mesmo. Só o PHP e o C# (dentre as que eu conheço melhor) tem esse tipo de 'mágica'...

Compartilhar este post


Link para o post
Compartilhar em outros sites

É, se formos pensar como um karaokê, com métodos mágicos aquela bolinha irritante que dita o compasso vai pular mais vezes, o que representa maior tempo de resposta.

 

Porém, como eu disse, basta não abusar dos métodos mágicos e também não precisa reverter um algoritimo de criptografia de 2048 bits dentro deles.

 

Tá, o exemplo foi forte, mas eu acho que, neles, um IF-zinho básico pararetornar o valor se enconrado ou FALSE se não, não prejudica tanto.

 

No meu caso, só duas classes usufurem de métodos mágicos: o Controller e a View.

 

A View pelo motivo acima exposto e o Controller usa o __set() e __unset() para operar ´sobre varuiáveis de Template, como se fosse um atalho para View; e __get() e __isset() para operar sobre os parâmetros GET.

Compartilhar este post


Link para o post
Compartilhar em outros sites

É, usado com moderação, não faz tanto mal assim.

Agora __call e __callStatic me doem na alma quando eu vejo...

Compartilhar este post


Link para o post
Compartilhar em outros sites

__callStatic() e __set_state eu nunca usei. Acho a maior bobeira.

 

Já o __call() é útil, de novo, se usado com moderação. No meu sistema uma única classe usa ele. E uso mais é por questão de legibilidade de código viu?

 

A classe em questão é uma abstrata para que, através de um método, não seja possível que um Request Header seja usado no contexto de uma Response e vice-versa.

 

Os cabeçalhos são objetos armazenados em uma Lista, que é uma Collection acessível por seus índices.

 

Essa classe tem um método getHeaders() que retorna todos os cebeçalhos armazenados e não onde eles estão armazenados (a Lista em si), então eu uso __call() para fazer essa "ponte".

 

Se não ficaria esquisito criar um método, sei lá, getList() e usar $headers -> getList() -> indexOf(), por exemplo.

 

Ao passo que $headers -> indexOf(), faz muito mais sentido.

Compartilhar este post


Link para o post
Compartilhar em outros sites

É ruim, você precisa conhecer a interface de uma classe. Esse tipo de recurso dificulta debug, documentação.

Outro programador, ou você mesmo pode não saber que é possível chamar um método sem ter que acessar a classe e ver que ela implementa esse método mágico.

Compartilhar este post


Link para o post
Compartilhar em outros sites

E uso mais é por questão de legibilidade de código viu?

 

Métodos mágicos e legibilidade são incompatíveis.

 

não seja possível que um Request Header seja usado no contexto de uma Response e vice-versa.

 

Está ai um cado de uso de State. (ex: http://forum.imasters.com.br/topic/386538-conexao-ssh-com-php-usando-state-design-pattern/)

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

É ruim, você precisa conhecer a interface de uma classe.

Mas se conhece-se a assinatura da classe Lists e a interface da classe AbstractHeaders, não dá no mesmo?

 

O programador que usar vai ter de simplesmente saber que há essa ponte, não?

 

Métodos mágicos e legibilidade são incompatíveis.

Depende do ponto de vista.

 

Pode ser frescura da minha parte, mas em tudo que se relaciona à Tecnologia eu prezo primeiro pela aparência e depois pela funcionalidade, quando a maioria das pessoas pensa ao contrário.

 

Seja um código seja um software a ser utilizado. Se ele é feio espartano, não necessariamente ele é ruim, mas se o programador não se deu ao trabalho de fazer uma interface caprichada, vai tornar o mesmo alguma coisa mais focada para geeks do que o usuário final.

 

Um bom exemplo disso é a FFMPEG ou o BeSweet, rodam em linha de comando, mas não são assim tão fáceis de usar quanto seriam com uma GUI.

 

Desde a primeira vez que você postou esse tópico sempre achei ele meio complexo demais.

 

Tá certo que na "época" eu não entendia tão bem do assunto quanto eu entendo hoje (não que seja assim um colosso de conhecimento também :P ), mas ainda assim, não vejo como aplicar esse padrão nesse caso.

 

Se puder clarificar, e a resposta não for considerados desvirtuamento de tópico, eu agradeç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.