Ultimamente, tenho tentado estudar PHP e acabo ficando preso em traços. Entendo o conceito de reutilização de código horizontal e não quero necessariamente herdar de uma classe abstrata. O que não entendo é: Qual é a diferença crucial entre o uso de características versus interfaces?
Tentei procurar por um artigo ou artigo decente explicando quando usar um ou outro, mas os exemplos que encontrei até agora parecem tão parecidos quanto idênticos.
Imagick
objetos, menos todo o inchaço necessário nos velhos tempos anteriores às características.Respostas:
Uma interface define um conjunto de métodos que a classe de implementação deve implementar.
Quando uma característica é encontrada
use
, as implementações dos métodos também aparecem - o que não acontece em umaInterface
.Essa é a maior diferença.
Na reutilização horizontal para RFC PHP :
fonte
Anúncio de serviço público:
Quero declarar que acredito que os traços são quase sempre um cheiro de código e devem ser evitados em favor da composição. É minha opinião que a herança única é frequentemente abusada a ponto de ser um antipadrão e a herança múltipla apenas compõe esse problema. Você será muito melhor atendido na maioria dos casos, favorecendo a composição sobre a herança (seja ela única ou múltipla). Se você ainda está interessado em características e sua relação com interfaces, continue lendo ...
Vamos começar dizendo o seguinte:
Para escrever código OO, você precisa entender que OOP é realmente sobre os recursos de seus objetos. Você precisa pensar nas aulas em termos do que elas podem fazer, e não do que realmente fazem . Isso contrasta fortemente com a programação processual tradicional, na qual o foco é fazer um pouco de código "fazer alguma coisa".
Se o código OOP é sobre planejamento e design, uma interface é o blueprint e um objeto é a casa totalmente construída. Enquanto isso, os traços são simplesmente uma maneira de ajudar a construir a casa estabelecida pelo projeto (a interface).
Interfaces
Então, por que devemos usar interfaces? Simplesmente, as interfaces tornam nosso código menos quebradiço. Se você duvida desta afirmação, pergunte a alguém que foi forçado a manter o código legado que não foi escrito nas interfaces.
A interface é um contrato entre o programador e seu código. A interface diz: "Enquanto você seguir minhas regras, poderá me implementar da maneira que quiser e prometo que não quebrarei seu outro código".
Por exemplo, considere um cenário do mundo real (sem carros ou widgets):
Você começa escrevendo uma classe para armazenar em cache as respostas das solicitações usando a APC:
Em seguida, no seu objeto de resposta HTTP, verifique se há um acerto no cache antes de fazer todo o trabalho para gerar a resposta real:
Essa abordagem funciona muito bem. Mas talvez algumas semanas depois você decida usar um sistema de cache baseado em arquivo em vez da APC. Agora você precisa alterar o código do seu controlador, porque o programou para trabalhar com a funcionalidade da
ApcCacher
classe, e não com uma interface que expresse os recursos daApcCacher
classe. Digamos que, em vez do acima, você tenha tornado aController
classe dependente de um emCacherInterface
vez do concreto, da seguinteApcCacher
forma:Para acompanhar, você define sua interface da seguinte maneira:
Por sua vez, você
ApcCacher
e suas novasFileCacher
classes implementamCacherInterface
e você programa suaController
classe para usar os recursos exigidos pela interface.Este exemplo (espero) demonstra como a programação em uma interface permite alterar a implementação interna de suas classes sem se preocupar se as alterações quebrarão seu outro código.
Traits
Traços, por outro lado, são simplesmente um método para reutilizar o código. As interfaces não devem ser pensadas como uma alternativa mutuamente exclusiva às características. De fato, a criação de características que atendam aos recursos exigidos por uma interface é o caso de uso ideal .
Você só deve usar características quando várias classes compartilham a mesma funcionalidade (provavelmente ditada pela mesma interface). Não faz sentido usar uma característica para fornecer funcionalidade para uma única classe: isso apenas ofusca o que a classe faz e um design melhor transfere a funcionalidade da característica para a classe relevante.
Considere a seguinte implementação de característica:
Um exemplo mais concreto: imagine que você
FileCacher
e seuApcCacher
da discussão da interface usem o mesmo método para determinar se uma entrada de cache é obsoleta e deve ser excluída (obviamente esse não é o caso na vida real, mas vá com ela). Você pode escrever uma característica e permitir que ambas as classes a usem para o requisito de interface comum.Uma última palavra de cautela: tenha cuidado para não exagerar nas características. Muitas vezes, as características são usadas como muleta para o design deficiente quando implementações de classe exclusivas são suficientes. Você deve limitar as características ao cumprimento dos requisitos da interface para obter o melhor design de código.
fonte
A
trait
é essencialmente a implementação de a de PHPmixin
e é efetivamente um conjunto de métodos de extensão que podem ser adicionados a qualquer classe através da adição detrait
. Os métodos então se tornam parte da implementação dessa classe, mas sem usar herança .No Manual do PHP (ênfase minha):
Um exemplo:
Com a característica acima definida, agora posso fazer o seguinte:
Neste ponto, quando eu crio uma instância da classe
MyClass
, ela tem dois métodos, chamadosfoo()
ebar()
- que vêmmyTrait
. E - observe que ostrait
métodos definidos já têm um corpo de método - que umInterface
método definido não pode.Além disso, o PHP, como muitas outras linguagens, usa um único modelo de herança - o que significa que uma classe pode derivar de várias interfaces, mas não de várias classes. No entanto, uma classe PHP pode ter várias
trait
inclusões - o que permite que o programador inclua partes reutilizáveis - como poderiam incluir várias classes base.Algumas coisas a serem observadas:
Polimorfismo:
No exemplo anterior, onde
MyClass
se estendeSomeBaseClass
,MyClass
é uma instância deSomeBaseClass
. Em outras palavras, uma matriz comoSomeBaseClass[] bases
pode conter instâncias deMyClass
. Da mesma forma, seMyClass
estendidaIBaseInterface
, uma matriz deIBaseInterface[] bases
poderia conter instâncias deMyClass
. Não existe essa construção polimórfica disponível com atrait
- porque atrait
é essencialmente apenas um código que é copiado para conveniência do programador em cada classe que a utiliza.Precedência:
Conforme descrito no manual:
Portanto, considere o seguinte cenário:
Ao criar uma instância do MyClass, acima, ocorre o seguinte:
Interface
IBase
requer uma função sem parâmetros chamadaSomeMethod()
a ser fornecida.BaseClass
fornece uma implementação desse método - satisfazendo a necessidade.trait
myTrait
fornece uma função sem parâmetros chamadaSomeMethod()
também, que tem precedência sobre aBaseClass
versãoclass
MyClass
fornece sua própria versão deSomeMethod()
- que tem precedência sobre atrait
versão -.Conclusão
Interface
não pode fornecer uma implementação padrão de um corpo de método, enquanto umtrait
pode.Interface
é um polimórfica , herdada construo - enquanto umtrait
não é.Interface
s podem ser usados na mesma classe, assim como múltiplostrait
s.fonte
mixin
- e, ao revisitar a abertura da minha resposta, atualizei para refletir isso. Obrigado por comentar, @BoltClock!Eu acho que
traits
é útil criar classes que contêm métodos que podem ser usados como métodos de várias classes diferentes.Por exemplo:
Você pode ter e usar esse método de "erro" em qualquer classe que use essa característica.
Enquanto
interfaces
você só pode declarar a assinatura do método, mas não o código de suas funções. Além disso, para usar uma interface, você precisa seguir uma hierarquia, usandoimplements
. Este não é o caso de características.É completamente diferente!
fonte
to_integer
provavelmente seria incluído em umaIntegerCast
interface porque não há uma maneira fundamentalmente semelhante de converter (inteligentemente) classes para um número inteiro.use Toolkit
você poder ter$this->toolkit = new Toolkit();
ou estou perdendo algum benefício da característica em si?Something
's recipiente que você fazif(!$something->do_something('foo')) var_dump($something->errors);
Para os iniciantes acima da resposta, pode ser difícil, esta é a maneira mais fácil de entender:
Traits
portanto, se você deseja ter
sayHello
função em outras classes sem recriar toda a função, pode usar traços,Legal certo!
Não apenas funções, você pode usar qualquer coisa na característica (função, variáveis, const ..). Além disso, você pode usar várias características:
use SayWorld,AnotherTraits;
Interface
portanto, é assim que a interface é diferente das características: você precisa recriar tudo na interface na classe implementada. interface não tem implementação. e interface só pode ter funções e const, não pode ter variáveis.
Eu espero que isso ajude!
fonte
Essa é uma boa maneira de pensar sobre isso na maioria das circunstâncias, mas há várias diferenças sutis entre as duas.
Para começar, o
instanceof
operador não trabalhará com características (ou seja, uma característica não é um objeto real), portanto você não pode ver se uma classe tem uma certa característica (ou se duas classes não relacionadas compartilham uma característica ) É isso que eles querem dizer com uma construção para reutilização de código horizontal.Agora, existem funções no PHP que permitem obter uma lista de todas as características que uma classe usa, mas a herança de características significa que você precisará fazer verificações recursivas para verificar com segurança se uma classe em algum momento tem uma característica específica (há exemplo código nas páginas de documentação do PHP). Mas sim, certamente não é tão simples e limpo como o instanceof, e IMHO é um recurso que tornaria o PHP melhor.
Além disso, as classes abstratas ainda são classes, portanto, não resolvem problemas de reutilização de códigos relacionados à herança múltipla. Lembre-se de que você só pode estender uma classe (real ou abstrata), mas implementar várias interfaces.
Eu descobri que características e interfaces são realmente boas de usar em conjunto para criar herança pseudo-múltipla. Por exemplo:
Fazer isso significa que você pode usar instanceof para determinar se o objeto Door específico é Keyed ou não, você sabe que obterá um conjunto consistente de métodos etc., e todo o código está em um único lugar em todas as classes que usam o KeyedTrait.
fonte
Os traços são simplesmente para reutilização de código .
A interface apenas fornece a assinatura das funções que devem ser definidas na classe em que podem ser usadas, dependendo da discrição do programador . Dando assim um protótipo para um grupo de classes .
Para referência - http://www.php.net/manual/en/language.oop5.traits.php
fonte
Você pode considerar uma característica como uma "copiar e colar" automatizada de código, basicamente.
O uso de características é perigoso, pois não há como saber o que ele faz antes da execução.
No entanto, os traços são mais flexíveis devido à falta de limitações, como herança.
As características podem ser úteis para injetar um método que verifica algo em uma classe, por exemplo, a existência de outro método ou atributo. Um bom artigo sobre isso (mas em francês, desculpe) .
Para as pessoas que conseguem ler em francês, a Revista GNU / Linux HS 54 tem um artigo sobre esse assunto.
fonte
Se você sabe inglês e sabe o que
trait
significa, é exatamente o que o nome diz. É um pacote de métodos e propriedades sem classe que você anexa a classes existentes, digitandouse
.Basicamente, você pode compará-lo a uma única variável. As funções de fechamento podem
use
essas variáveis de fora do escopo e dessa maneira elas têm o valor dentro. Eles são poderosos e podem ser usados em tudo. O mesmo acontece com as características se elas estiverem sendo usadas.fonte
Outras respostas fizeram um ótimo trabalho ao explicar as diferenças entre interfaces e características. Vou me concentrar em um exemplo útil do mundo real, em particular um que demonstra que os traços podem usar variáveis de instância - permitindo adicionar comportamento a uma classe com o mínimo de código padrão.
Novamente, como mencionado por outros, os traços combinam bem com as interfaces, permitindo que a interface especifique o contrato de comportamento e a característica para cumprir a implementação.
Adicionar recursos de publicação / assinatura de eventos a uma classe pode ser um cenário comum em algumas bases de código. Existem 3 soluções comuns:
use
a característica, também conhecida como importá-la, para obter os recursos.Quão bem cada um funciona?
# 1 não funciona bem. Até o dia em que você perceber que não pode estender a classe base, porque já está estendendo outra coisa. Não mostrarei um exemplo disso, porque deve ser óbvio o quão limitado é usar herança como esta.
# 2 e # 3 funcionam bem. Vou mostrar um exemplo que destaca algumas diferenças.
Primeiro, algum código que será o mesmo entre os dois exemplos:
Uma interface
E algum código para demonstrar o uso:
Ok, agora vamos mostrar como a implementação da
Auction
classe será diferente ao usar traços.Primeiro, veja como seria o número 2 (usando composição):
Veja como o número 3 (traços) seria:
Observe que o código dentro da
EventEmitterTrait
é exatamente o mesmo que está dentro daEventEmitter
classe, exceto que a característica declara otriggerEvent()
método como protegido. Portanto, a única diferença que você precisa observar é a implementação daAuction
classe .E a diferença é grande. Ao usar a composição, obtemos uma ótima solução, permitindo que reutilizemos o nosso
EventEmitter
por quantas classes quisermos. Mas, a principal desvantagem é que temos muito código clichê que precisamos escrever e manter, pois para cada método definido naObservable
interface, precisamos implementá-lo e escrever código clichê chato que apenas encaminha os argumentos para o método correspondente em nosso compôs oEventEmitter
objeto. O uso da característica neste exemplo nos permite evitar isso , ajudando-nos a reduzir o código padrão e melhorar a capacidade de manutenção .No entanto, pode haver momentos em que você não deseja que sua
Auction
classe implemente aObservable
interface completa - talvez você queira apenas expor 1 ou 2 métodos, ou talvez nenhum, para que você possa definir suas próprias assinaturas de método. Nesse caso, você ainda pode preferir o método de composição.Mas, a característica é muito atraente na maioria dos cenários, especialmente se a interface tiver muitos métodos, o que faz com que você escreva muitos clichês.
* Você pode realmente fazer as duas coisas - defina a
EventEmitter
classe caso queira usá-la em termos de composição e defina aEventEmitterTrait
característica também, usando aEventEmitter
implementação da classe dentro da característica :)fonte
A característica é a mesma que uma classe que podemos usar para vários propósitos de herança e também reutilização de código.
Podemos usar características dentro da classe e também podemos usar várias características na mesma classe com 'use keyword'.
A interface está usando para reutilização de código o mesmo que uma característica
a interface é estender várias interfaces para que possamos resolver os problemas de herança múltipla, mas quando implementamos a interface, devemos criar todos os métodos dentro da classe. Para mais informações, clique no link abaixo:
http://php.net/manual/en/language.oop5.traits.php http://php.net/manual/en/language.oop5.interfaces.php
fonte
Uma interface é um contrato que diz "esse objeto é capaz de fazer isso", enquanto uma característica está dando ao objeto a capacidade de fazer a coisa.
Uma característica é essencialmente uma maneira de "copiar e colar" o código entre as classes.
Tente ler este artigo, Quais são as características do PHP?
fonte
A principal diferença é que, com interfaces, você deve definir a implementação real de cada método dentro de cada classe que implementa a referida interface, para que muitas classes implementem a mesma interface, mas com comportamento diferente, enquanto traços são apenas pedaços de código injetado em uma aula; outra diferença importante é que os métodos de características podem ser apenas métodos de classe ou métodos estáticos, diferentemente dos métodos de interface que também podem (e geralmente são) ser métodos de instância.
fonte