Compreender e aplicar Polimorfismo em PHP

Na programação orientada a objetos, polimorfismo é uma ferramenta poderosa e fundamental. Ele pode ser usado para criar um fluxo mais orgânica em sua aplicação. Este tutorial irá descrever o conceito geral de polimorfismos, e como ele pode ser facilmente implementado em PHP.


O que é polimorfismo?

Polimorfismo é uma palavra muito tempo para um conceito muito simples.

Polimorfismo descreve um padrão de programação orientada a objeto em que as classes têm diferentes funcionalidades ao compartilhar de uma interface comum.

A beleza do polimorfismo é que o código de trabalho com as diferentes classes, não precisa saber qual a classe que está usando desde que todos eles são usados da mesma forma.

A analogia com o mundo real para o polimorfismo é um botão. Toda a gente sabe como usar um botão: você simplesmente pressionar a ele. O que um botão "não", entretanto, depende do que ele está conectado e do contexto em que ela é usada - mas o resultado não afeta a forma como é utilizado. Se o seu patrão lhe diz para pressionar um botão, você já tem todas as informações necessárias para executar a tarefa.

No mundo da programação, polimorfismo é usado para fazer aplicações mais modular e extensível. Em vez de declarações condicionais bagunçado descrevendo os diferentes cursos de ação, você cria objetos intercambiáveis que você selecionar com base em suas necessidades. Esse é o objetivo básico de polimorfismo.


Interfaces

Uma parte integrante do polimorfismo é a interface comum. Existem duas maneiras de definir um interface em PHP: interfaces e classes abstratas. Ambos têm seus usos, e você pode misturar e combinar-los como quiser em sua hierarquia de classe.

Usando uma interface

Uma interface é similar a uma classe, exceto que ele não pode conter código. Pode definir nomes de métodos e argumentos, mas não o conteúdo dos métodos. Todas as classes que implementam uma interface deve implementar todos os métodos definidos pela interface. Uma classe pode implementar várias interfaces.

Uma interface é declarado usando a ' interface 'palavra-chave:

 (interface MyInterface
/ / Métodos
)

e está associado a uma classe usando o ' implements 'palavra-chave:

 MyClass classe implementa (MyInterface
/ / Métodos
)

Métodos podem ser definidos na interface, tal como em uma classe, exceto sem o corpo (a parte entre as chaves):

 (interface MyInterface
DoThis função pública ();
doThat função pública ();
setName função pública ($ name);
)

Todos os métodos definidos aqui terá de ser incluído em qualquer implementação de classes exatamente como descrito. (Nota: os comentários de código abaixo.)

 / / Válido
MyClass classe implementa (MyInterface
$ nome pública;
público DoThis function () (
/ / Código que faz isso
)
doThat função pública () (
/ / Código que faz isso
)
setName função pública ($ name) (
$ This-> nome = $ nome;
)
)

/ / Inválido
MyClass classe implementa (MyInterface
/ / DoThis falta ()!
doThat função pública () (
/ / Este deve ser público!
)
público setName function () (
/ / O argumento de falta nome!
)
)

Usando uma classe abstrata

Uma classe abstrata é uma mistura entre uma interface e uma classe. Pode definir a funcionalidade, bem como interface (na forma de métodos abstratos). Classes estender uma classe abstrata deve implementar todos os métodos abstratos definidos na classe abstrata.

Uma classe abstrata é declarada da mesma forma que as classes com a adição do " abstract palavra-chave ":

 MyAbstract classe abstrata (
/ / Métodos
)

e está associado a uma classe usando o ' extends 'palavra-chave:

 Class MyClass (estende MyAbstract
/ / Métodos da classe
)

métodos regulares podem ser definidos em uma classe abstrata, tal como em uma classe regular, bem como de quaisquer métodos abstratos (usando o ' abstract 'palavra-chave). métodos abstratos se comportam exatamente como os métodos definidos em uma interface, e deve ser implementado exatamente como definido pela extensão classes.

 MyAbstract classe abstrata (
protected $ nome;
público DoThis function () (
/ / Fazer isso
)
resumo doThat função privada ();
resumo setName função pública ($ name);
)

Passo 1: identificar o problema

Vamos imaginar que você tem um Article classe que é responsável pela gestão de artigos em seu site. Ele contém informações sobre um artigo, incluindo o título, autor, data e categoria. Aqui está o que parece:

 poly_base_Article classe (
$ titulo pública;
autor $ público;
$ date pública;
categoria $ público;

public function __construct ($ title, $ autor, data, $ categoria = 0) (
$ This-> title = $ title;
$ This-> author = $ autor;
$ This-> data = $ data;
$ This-> categoria = $ categoria;
)
)

Nota: As classes de exemplo neste tutorial usa a convenção de nomenclatura de "package_component_Class." Esta é uma maneira comum de classes separadas em namespaces virtuais para evitar colisões de nomes.

Agora você quer adicionar um método para a produção da informação em diferentes formatos, como XML e JSON. Você pode estar tentado a fazer algo como isto:

 poly_base_Article classe (
//...
public function escreve ($ type) (
''Ret = $;
switch ($ tipo) (
caso 'XML':
$ ret = '<article>;
$ ret .= '<title>. título $ obj->. '</ Title> ";
$ ret .= '<author>. autor $ obj->. '</ Autor>';
$ ret .= '<data>. data $ obj->. '</ Data>';
$ ret .= '<category>. categoria $ obj->. "</> Categoria ';
$ ret .= '</ artigo>';
break;
case 'JSON':
array $ array = ('artigo' => $ obj);
$ Ret = json_encode ($ array);
break;
)
return $ ret;
)
)

Esta é uma espécie de solução feio, mas funciona - por agora. Pergunte a si mesmo o que acontece no futuro, no entanto, quando queremos adicionar mais formatos? Você pode continuar editando a classe, adicionando mais e mais casos, mas agora você só está diluindo sua classe.

Um princípio importante da OOP é que uma classe deve fazer uma coisa, e deve fazê-lo bem.

Com isto em mente, as declarações condicional deve ser uma bandeira vermelha, indicando que a classe está tentando fazer muitas coisas diferentes. Este é o lugar onde o polimorfismo vem dentro

No nosso exemplo, é claro que existem duas tarefas apresentadas: artigos de gestão e formatação de seus dados. Neste tutorial, vamos refazer a nossa vontade de formatação de código em um novo conjunto de classes e descubra como é fácil utilizar polimorfismo.


Passo 2: Defina sua interface

A primeira coisa que devemos fazer é definir a interface. É importante pensar muito sobre a sua interface, pois qualquer alteração pode exigir alterações para o código de chamada. Neste exemplo, vamos estar usando uma interface simples para definir o nosso método um:

 poly_writer_Writer (interface
public function escreve (poly_base_Article $ obj),
)

É tão simples, temos um público definido write() método que aceita um objeto como um argumento do artigo. Qualquer classe que implementa a interface do escritor ter a certeza de que este método.

Dica: Se você quiser restringir o tipo de argumentos que podem ser passados para as funções e métodos, você pode usar as dicas, tipo como fizemos no write() método, que só aceita objetos do tipo poly_base_Article . Infelizmente, o tipo de retorno insinuando não é suportado nas versões atuais do PHP, por isso é até você para tomar conta de valores de retorno.


Passo 3: Criar a sua implementação

Com sua interface definida, é hora de criar as classes que realmente fazem coisas. No nosso exemplo, temos dois formatos que queremos para a saída. Assim, temos duas classes Escritor: XMLWriter e JSONWriter. Cabe a estes para extrair os dados do objeto do artigo passou e formatar as informações.

Aqui está o que parece XMLWriter:

 poly_writer_XMLWriter classe implementa (poly_writer_Writer
public function escreve (poly_base_Article $ obj) (
$ ret = '<article>;
$ ret .= '<title>. título $ obj->. '</ Title> ";
$ ret .= '<author>. autor $ obj->. '</ Autor>';
$ ret .= '<data>. data $ obj->. '</ Data>';
$ ret .= '<category>. categoria $ obj->. "</> Categoria ';
$ ret .= '</ artigo>';
return $ ret;
)
)

Como você pode ver a partir da declaração da classe, nós usamos a implements -chave para implementar a nossa interface. O write() método contém funcionalidades específicas para a formatação XML.

Agora aqui é a nossa classe JSONWriter:

 poly_writer_JSONWriter classe implementa poly_writer_Writer (
public function escreve (poly_base_Article $ obj) (
array $ array = ('artigo' => $ obj);
json_encode retorno ($ array);
)
)

Todos os nossos código específico para cada formato agora está contida dentro de classes individuais. Essas classes têm cada um a responsabilidade de lidar com um formato específico, e nada mais. Nenhuma outra parte do seu aplicativo precisa se preocupar com a forma como estes trabalhos, a fim de usá-lo, graças à nossa interface.


Etapa 4: Use a sua implementação

Com a nossa nova classe definida, é hora de rever a nossa classe artigo. Todo o código que viveu no original write() método tem sido fatorado em nosso novo conjunto de classes. Todos os nosso método tem a fazer agora é usar as novas classes, como este:

 poly_base_Article classe (
//...
public function escreve ($ poly_writer_Writer escritor) (
retorno escrever $ escritor-> ($ this);
)
)

Todos esse método não é agora aceitar um objeto da classe Writer (ou seja, qualquer classe que implementa a interface do Writer), o seu apelo write() método, passando-se ( $this ) como argumento, em seguida, encaminhar o valor de retorno direto para o cliente código. Ele não precisa mais se preocupar com os detalhes da formatação de dados, e pode se concentrar em sua tarefa principal.

Obtenção de um escritor

Você pode estar se perguntando onde você obtém um objeto Writer, para começar, pois você precisa passar uma a este método. Isso é até você, e há muitas estratégias. Por exemplo, você pode usar uma classe de fábrica para pegar dados do pedido e criar um objeto:

 poly_base_Factory classe (
getWriter função pública estático () (
/ / Pega variável pedido
formato formato = $ _REQUEST ['];
/ / Construir o nosso nome da classe e verificar a sua existência
$ class = "poly_writer_. formato $. "Escritor";
if (class_exists ($ class)) (
/ / Retorna um objeto novo escritor
novo retorno $ (classe);
)
/ / Caso contrário, não
throw new Exception ("formato não suportado");
)
)

Como eu disse, existem muitas outras estratégias para usar dependendo de suas necessidades. Neste exemplo, uma variável de solicitação escolhe qual o formato a utilizar. Ele constrói um nome de classe da variável pedido, verifica se ela existe, em seguida, retorna um objeto novo escritor. Se não existe nenhum com esse nome, uma exceção é acionada para deixar o código do cliente descobrir o que fazer.


Passo 5: Put It All Together

Com tudo em seu lugar, aqui é como o nosso código do cliente que juntar tudo isso:

 artigo = $ poly_base_Article novo ('polimorfismo "," Steve "tempo, (), 0);

try (
escritor $ = poly_base_Factory: getWriter ();
)
catch (Exception $ e) (
escritor poly_writer_XMLWriter $ = new ();
)

echo $ artigo-> write ($ escritor);

Primeiro criamos um artigo exemplo de objeto para trabalhar com ele. Então, tentamos obter um objeto Writer da fábrica, caindo de volta a um padrão (XMLWriter) se uma exceção é lançada. Finalmente, passar o objeto de nosso artigo do escritor de write() método, a impressão do resultado.


Conclusão

Neste tutorial, eu tenho desde que com uma introdução ao polimorfismo e uma explicação de interfaces em PHP. Eu espero que você perceber que eu só mostrei um caso de uso potencial para o polimorfismo. Há muitas, muitas outras aplicações. O polimorfismo é uma maneira elegante de escapar feio declarações condicionais no código POO. Ele segue o princípio da conservação dos seus componentes em separado, e é parte integrante dos padrões de design. Se você tiver alguma dúvida, não hesite em perguntar nos comentários!

Nenhum comentário: