sexta-feira, 16 de setembro de 2011

PHPog: uma questão de design?


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

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:
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.
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:
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?
Fonte: imasters.com.br

Nenhum comentário :

Postar um comentário

Total de visualizações de página