Polimorfismo e PHP 5
por Olavo Alexandrino

Amante de PHP e de Orientação à Objetos resolvi publicar este artigo e mostrar como a nova versão do PHP implementa este conceito poderoso de Linguagens Orientada a Objetos. Vale a pena conferir!



Sempre foi grande a queixa por parte dos desenvolvedores o fraco suporte à Programação Orientação a Objetos (POO) que o PHP 4 oferecia. A falta de recursos como: modificadores explícitos (public, private), classes abstratas, interfaces, chamadas de objetos com várias hierarquias, e outras muitas características fazem bastante falta quando o assunto é o Desenvolvimento Orientado a Objetos. Pra se ter uma idéia o VBScript, a linguagem padrão do ASP (Active Server Pages da Microsoft), já tinha modificadores como private e public.

Aí veio o PHP 5 para resolver nossos problemas. Ou melhor, resolver boa parte de nossos problemas. Foram adicionadas grandes melhorias no que se diz respeito à POO, porém devemos saber que o passo dado não foi definitivo pois existem várias características de POO que não foram ainda totalmente definidas.

Bem, o propósito deste artigo não é discutir sobre as novas funcionalidades do PHP 5 e nem definir os vários conceitos de POO e sim falar apenas de um deles: O Polimorfismo, e saber como ele pode ser implementado no PHP 5.

Você pode ter mais explicações mais detalhadas sobre as mudanças do PHP 4 pra 5 neste link:
http://www.phpbrasil.com/articles/article.php/id/831


Então vamos lá! Falaremos de Polimorfismo! O que seria isso? Vamos pela definição do nome: polimorfo + ismo. Que é uma referência à palavra grega “polymorphos”, ou seja, “que se apresenta sobre numerosas formas”, sujeito a “variar de forma”(3).

Então o que teríamos em programação ? Seria a característica que um objeto teria de apresentar diferentes comportamentos em situações ou contextos diferentes. A idéia de Polimorfismo está estritamente ligada à existência de interfaces (uma das características de uma Linguagem OO). Pois é a partir delas que podemos “classificar” famílias de objetos que tem comportamentos em comum porém tem implementações diferentes. Queremos executar a mesma ação de um determinado objeto, qualquer que seja o seu tipo.


Por exemplo:

Imagine 3 classes tais como: Pessoa, Carro e Arvore. Será que poderíamos ter alguma relação entre elas? De cara pensamos que não. Mas temos sim !! Todos os três objetos poderiam ter um método chamado “getIdade()” e cada um poderia implementar este método à sua maneira, porém o retorno seria o mesmo: um inteiro que representa a idade do objeto em questão.

A classe Pessoa teria a idade de acordo com a diferença da data atual com a data de nascimento; A classe Carro poderia considerar além da data de compra a quilometragem corrente e fazer um cálculo pra definir a idade;
A classe Arvore consideraria a altura e o tipo de clima para definir a idade.

Mas todos os métodos retornariam apenas uma informação: UM INTEIRO QUE REPRESENTA A IDADE. A idéia então seria fazer uma Interface que definiria as propriedades e/ou comportamentos em comum a esse trio de objetos.

Evidentemente o exemplo acima não é tão claro, pois é tanto quanto estranho esse três objetos da vida real ser um exemplo de implementação aplicado em sistemas pelo mundo. Então darei outro exemplo bem simples e mais objetivo, e de facílimo entendimento: os objetos “Circulo” e “Quadrado”.

O que eles tem em comum?
Os dois são “Figuras” não são? Sim!!


O que um quadrado e um círculo tem mais em comum?
Se podemos desenhá-los em um plano então podemos obter a “Area” que ele ocupa nesse plano.

Finalmente temos:

- Nome de nossa Interface: IFigura
- Comportamento que ela define: getArea()

OBS: O “I” antes do nome “Figura” é um padrão utilizado que diz que se refere a uma (I) “Interface”.

Como o próprio site PHP.NET diz:

“Interfaces de Objetos permite a criação de código que especifica quais métodos e variáveis uma classe deve implementar, sem ter que definir como esses métodos serão tratados.(2)”

Nossa interface especifica apenas o método: getArea()

- Nossas Classes: Circulo e Quadrado

Nossas classes implementam a interface “IFigura” e assim como a definição devem também implementar obrigatoriamente as propriedades e comportamentos da interface herdada.

Assim, teremos o Polimorfismo, ou seja, “o programador pode tratar de generalidades e deixar o próprio ambiente de tempo de execução se preocupar com os detalhes específicos. O programador pode comandar uma ampla variedade de objetos, para se comportarem de maneira apropriada a esses objetos, mesmo que não conheça o tipos. (1) pagina 335”



Aonde o Polimorfismo entra ?!!

Agora paremos e pensemos como seria o exemplo acima se por acaso tivéssemos não apenas 2 tipos de objetos e sim 10?

E se desejássemos obter, por algum motivo, a área de todos num conjunto de 100 objetos sortidos em qualquer proporção desses 10 tipos. Como faríamos?

Primeiro todos os 10 tipos (10 classes de tipos de figuras) implementariam nossa interface. Pois todos os objetos dessa classe são uma figura!

Já que todos são figuras, os colocaríamos num ARRAY de objetos ( array de IFigura ). A cada iteração teríamos acesso a um item do ARRAY aonde acessaríamos:

$arrObjsIFigura[ index ]->getArea();

Nesse momento NÃO saberíamos qual tipo de fato que contém aquele índice. Mas como TODOS os índices do array estão preenchidos com objetos do tipo “IFigura” ele teria a implementação do “getArea()”. A cada iteração o ARRAY poderia ter um tipo diferente e ele teria uma maneira POLIMORFICA de calcular a área do objeto corrente, pois sabemos que cada objeto implementa o comportamento de maneira diferente.

Mas um problema surge! A tipagem da linguagem. Como sabemos uma das características de “linguagens de script” é que as variáveis são do tipo “variant”, ou seja, seu tipo é definido em tempo de atribuição e muitas vezes são convertidas em tempo de execução. Essa característica impede que o conceito de Polimorfismo seja aplicado de maneira perfeita pois não teríamos garantia de ter um array de objetos de um único tipo já que, como sabemos, o array de PHP aceita qualquer que seja os atributos.

Definições como a da listagem abaixo são inconcebíveis em linguagens como Java e C#, pois elas são fortemente tipadas.

<?php
$arr = array("foo" => "bar", 12 => true);

echo $arr["foo"]; // bar
echo $arr[12]; // 1
?>


Porém, como estamos falando de PHP 5, a coisa é um pouquinho diferente. Pois essa versão introduz o conceito de “Indução de Tipo”, no qual funções podem forçar que parâmetros sejam de objetos específicos! E isso é formidável! E resolve em partes nosso problema, pois podemos GARANTIR que todos os itens de nosso array são do tipo “IFigura”.


Então vamos a implementação!


<?php
/** 
 * Nossa simples interface que define apenas um comportamento: o getArea()
 * @author Olavo Alexandrino <oalexandrino@yahoo.com.br>
 * @copyright Copyright © 2004, oalexandrino.com
 */
interface IFigura
{
    
/** 
     * Nome do método a ser implementado. (Aqui NÃO definimos o corpo do método, apenas assinatura)
     */
    
public function getArea();
}
?>


 
<?php
/** 
 * Classe Circulo
 * @author Olavo Alexandrino <oalexandrino@yahoo.com.br>
 * @copyright Copyright © 2004, oalexandrino.com
 */
class Circulo implements IFigura
{
    
private $raio;

    
/** 
     * Construtor
     */
    
function Circulo$intValue )
    {
        
$this->raio = (double) $intValue;
    }

    
/** 
     * Area do Circulo
     * @return double    
     */    
    
public function getArea()
    {
        return 
pow$this->raio2) * 3.14;
    }
}
?>



<?php
/** 
 * Classe Quadrado
 * @author Olavo Alexandrino <oalexandrino@yahoo.com.br>
 * @copyright Copyright © 2004, oalexandrino.com
 */
class Quadrado implements IFigura
{
    
private $a;
    
    
/** 
     * Construtor
     */    
    
function Quadrado$intValue )
    {
        
$this->= (double) $intValue;
    }
    
    
/** 
     * Area do Quadrado
     * @return double    
     */        
    
public function getArea()
    {
        return (
$this->$this->a);
    }
}
?>



<?php
/** 
 * Classe FiguraPolimorfismo que GARANTE que apenas objetos que implemetem a IFigura sejam adicionados ao array
 * @author Olavo Alexandrino <oalexandrino@yahoo.com.br>
 * @copyright Copyright © 2004, oalexandrino.com
 */
class FiguraPolimorfismo
{
    
/** 
     * Adiciona um item ao array
     * @param array()
     * @param IFigura
     */
    
public function addItem( &$arrIFigura IFigura $objFigura)
    {
        
array_push($arrIFigura$objFigura);
    }
}
?>


TESTES

Agora vamos ao teste!


<?php
$objQuadrado  
= new Quadrado(2);
$objCirculo   = new Circulo(2);
$objCirculo2  = new Circulo(2.5);
$objQuadrado2  = new Quadrado(2.2);

$arrIFigura = array();
FiguraPolimorfismo::addItem$arrIFigura$objQuadrado );
FiguraPolimorfismo::addItem$arrIFigura$objCirculo );
FiguraPolimorfismo::addItem$arrIFigura$objCirculo2 );
FiguraPolimorfismo::addItem$arrIFigura$objQuadrado2 );


print 
"Imprimimos sem saber qual objeto estamos lidando: " "<br><br>";
for (
$i 0$i count($arrIFigura); $i++)
{
    print 
$arrIFigura[$i]->getArea(). "<br>";
}
?>


Veja que a passagem por referência garante que estamos liadando com o mesmo array, e a “Indução de Tipo” no método “addItem”, nos GARANTE que apenas Objetos do Tipo “IFigura” sejam adicionados a este array!

Faremos a Prova!


<?php
/** 
 * Classe Laranja
 * @author Olavo Alexandrino <oalexandrino@yahoo.com.br>
 * @copyright Copyright © 2004, oalexandrino.com
 */
class Laranja
{

}

$objLaranja =  new Laranja();
FiguraPolimorfismo::addItem$arrIFigura$objLaranja );
?>


E vejam o erro que é gerado!

Fatal error: Argument 2 must implement interface IFigura in D:\sites\php5\Polimorfismo\teste.php on line 87




Mais uma Prova!


<?php
/** 
 * Classe Losango
 * @author Olavo Alexandrino <oalexandrino@yahoo.com.br>
 * @copyright Copyright © 2004, oalexandrino.com
 */
class Losango implements IFigura
{
    
public function getArea()
    {
        return 
"Implementação do método! Faça Você!";
    }
}

$objLosango =  new Losango();
FiguraPolimorfismo::addItem$arrIFigura$objLosango );

echo 
"<br><br>";

for (
$i 0$i count($arrIFigura); $i++)
{
    print 
$arrIFigura[$i]->getArea(). "<br>";
}
?>


Vemos que o nosso NOVO tipo de objeto Losango foi ACEITO e pode ser adicionado ao nosso ARRAY TIPADO de objetos IFigura.

Assim podemos concluir, que dessa forma, o PHP 5 consegue trabalhar da forma que linguagens fortemente tipadas. Pode não ser o perfeito, mas é um avanço extraordinário para esta nova versão.


Como em nossa vida tudo é aprendizado, quem quiser opinar, criticar ou perguntar é só fazer!

Até a Próxima!
Olavo Alexandrino
Recife – PE
http://oalexandrino.com/



Artigo testado usando:

PHP Version 5.0.2
Apache/2.0.50 (Win32)
Microsoft Windows Server 2003 (Funciona legal !!)


Referências:

1 - Deitel, H. M

C# Como Programar - HARVEY M. DEITEL & PAUL J. DEITEL & LISTFIELD
Editora: Makron Books
ISBN: 8534614598
Ano: 2003
Edição: 1

2 - PHP.NET

http://br.php.net/manual/pt_BR/ref.array.php
http://br.php.net/manual/pt_BR/function.array-push.php
http://br.php.net/manual/pt_BR/language.oop5.typehinting.php
http://br.php.net/manual/pt_BR/language.oop5.php
http://www.php.net/manual/pt_BR/language.oop5.interfaces.php

3 – Novo Aurélio – O dicionário da Língua Portuguesa
Ferreira, Aurélio Buarque de Holanda.
Editora Nova Fronteira