son que poo new los inner php interface traits

php - que - Rasgos vs. Interfaces



php interface constants (13)

Una metáfora que se usa con frecuencia para describir los rasgos es que los rasgos son interfaces con la implementación.

Esta es una buena manera de pensar en ello en la mayoría de las circunstancias, pero hay una serie de diferencias sutiles entre los dos.

Para empezar, el operador instanceof no trabajará con rasgos (es decir, un rasgo no es un objeto real), por lo que no puede ver eso para ver si una clase tiene un rasgo determinado (o para ver si dos clases no relacionadas de otra manera comparten una rasgo). Eso es lo que quieren decir por ser una construcción para la reutilización de código horizontal.

Ahora hay funciones en PHP que le permitirán obtener una lista de todos los rasgos que usa una clase, pero la herencia de rasgos significa que tendrá que hacer verificaciones recursivas para verificar de manera confiable si una clase en algún punto tiene un rasgo específico (hay un ejemplo código en las páginas de doco de PHP). Pero sí, ciertamente no es tan simple y limpio como instancia de, y en mi humilde opinión es una característica que haría que PHP sea mejor.

Además, las clases abstractas siguen siendo clases, por lo que no resuelven problemas de reutilización de códigos relacionados con herencia múltiple. Recuerde que solo puede extender una clase (real o abstracta) pero implementar múltiples interfaces.

Descubrí que los rasgos y las interfaces son realmente buenos para usarlos de la mano para crear una herencia pseudo múltiple. P.ej:

class SlidingDoor extends Door implements IKeyed { use KeyedTrait; [...] // Generally not a lot else goes here since it''s all in the trait }

Hacer esto significa que puede usar instanceof para determinar si el objeto Door en particular está o no activado, sabe que obtendrá un conjunto consistente de métodos, etc., y que todo el código está en un lugar en todas las clases que usan KeyedTrait.

Últimamente he estado tratando de estudiar PHP, y me encuentro colgado de rasgos. Entiendo el concepto de reutilización de código horizontal y no querer heredar necesariamente de una clase abstracta. Lo que no entiendo es ¿cuál es la diferencia crucial entre el uso de rasgos frente a las interfaces?

He intentado buscar una publicación de blog decente o un artículo que explique cuándo usar una u otra, pero los ejemplos que he encontrado hasta ahora parecen tan similares como idénticos.

¿Podría alguien por ahí compartir su opinión / opinión sobre esto?


Básicamente, puede considerar un Rasgo como un "copiar y pegar" automatizado de código.

Usar Rasgos es peligroso ya que no hay ningún medio para saber qué hace antes de la ejecución.

Sin embargo, los Rasgos son más flexibles debido a su falta de limitaciones, como la herencia.

Los rasgos pueden ser útiles para inyectar un método que comprueba algo en una clase, por ejemplo. La existencia de otro método o atributo. Un buen artículo sobre eso (pero en francés, lo siento)

Para las personas que pueden leerlo en francés, la revista GNU / Linux HS 54 tiene un artículo sobre este tema.


Creo que los traits son útiles para crear clases que contienen métodos que pueden usarse como métodos de varias clases diferentes.

Por ejemplo:

trait ToolKit { public $errors = array(); public function error($msg) { $this->errors[] = $msg; return false; } }

Puede tener y usar este método de "error" en cualquier clase que use este rasgo.

class Something { use Toolkit; public function do_something($zipcode) { if (preg_match(''/^[0-9]{5}$/'', $zipcode) !== 1) return $this->error(''Invalid zipcode.''); // do something here } }

Mientras que con las interfaces solo puede declarar la firma del método, pero no el código de sus funciones. Además, para utilizar una interfaz debe seguir una jerarquía, utilizando implements . Este no es el caso de los rasgos.

¡Es completamente diferente!


El rasgo es el mismo que una clase que podemos usar para propósitos de herencia múltiple y también para la reutilización del código.

Podemos usar el rasgo dentro de la clase y también podemos usar varios rasgos en la misma clase con ''use keyword''.

La interfaz se utiliza para la reutilización del código igual que un rasgo

la interfaz es extender varias interfaces para que podamos resolver los múltiples problemas de herencia, pero cuando implementamos la interfaz, debemos crear todos los métodos dentro de la clase. Para más información haga clic en el siguiente enlace:

http://php.net/manual/en/language.oop5.traits.php http://php.net/manual/en/language.oop5.interfaces.php


La principal diferencia es que, con las interfaces, debe definir la implementación real de cada método dentro de cada clase que implementa dicha interfaz, de modo que pueda tener muchas clases implementando la misma interfaz pero con un comportamiento diferente, mientras que los rasgos son solo fragmentos de código inyectados una clase; otra diferencia importante es que los métodos de rasgo solo pueden ser métodos de clase o métodos estáticos, a diferencia de los métodos de interfaz que también pueden ser métodos de instancia (y generalmente lo son).


Otras respuestas hicieron un gran trabajo al explicar las diferencias entre interfaces y rasgos. Me centraré en un ejemplo útil del mundo real, en particular uno que demuestre que los rasgos pueden usar variables de instancia, lo que le permite agregar comportamiento a una clase con un mínimo de código.

Nuevamente, como mencionaron otros, los rasgos se combinan bien con las interfaces, lo que permite que la interfaz especifique el contrato de comportamiento y el rasgo para cumplir la implementación.

Agregar capacidades de publicación / suscripción de eventos a una clase puede ser un escenario común en algunas bases de código. Hay 3 soluciones comunes:

  1. Defina una clase base con el código de publicación / sub evento, y luego las clases que quieran ofrecer eventos pueden extenderla para obtener las capacidades.
  2. Defina una clase con código de publicación / subcontenido de eventos, y luego otras clases que quieran ofrecer eventos pueden usarla a través de la composición, definiendo sus propios métodos para envolver el objeto compuesto, dirigiendo las llamadas al método.
  3. Defina un rasgo con el código de publicación / sub evento, y luego otras clases que quieran ofrecer eventos pueden use el rasgo, también importarlo, para obtener las capacidades.

¿Qué tan bien funciona cada uno?

# 1 no funciona bien. Lo haría, hasta el día en que te des cuenta de que no puedes extender la clase base porque ya estás extendiendo otra cosa. No mostraré un ejemplo de esto porque debería ser obvio lo limitante que es usar una herencia como esta.

# 2 y # 3 ambos funcionan bien. Voy a mostrar un ejemplo que destaca algunas diferencias.

Primero, algún código que será igual entre ambos ejemplos:

Una interfaz

interface Observable { function addEventListener($eventName, callable $listener); function removeEventListener($eventName, callable $listener); function removeAllEventListeners($eventName); }

Y algunos códigos para demostrar el uso:

$auction = new Auction(); // Add a listener, so we know when we get a bid. $auction->addEventListener(''bid'', function($bidderName, $bidAmount){ echo "Got a bid of $bidAmount from $bidderName/n"; }); // Mock some bids. foreach ([''Moe'', ''Curly'', ''Larry''] as $name) { $auction->addBid($name, rand()); }

Ok, ahora vamos a mostrar cómo la implementación de la clase de Auction diferirá cuando se usen rasgos.

Primero, así es como se vería # 2 (usando composición):

class EventEmitter { private $eventListenersByName = []; function addEventListener($eventName, callable $listener) { $this->eventListenersByName[$eventName][] = $listener; } function removeEventListener($eventName, callable $listener) { $this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) { return $existingListener === $listener; }); } function removeAllEventListeners($eventName) { $this->eventListenersByName[$eventName] = []; } function triggerEvent($eventName, array $eventArgs) { foreach ($this->eventListenersByName[$eventName] as $listener) { call_user_func_array($listener, $eventArgs); } } } class Auction implements Observable { private $eventEmitter; public function __construct() { $this->eventEmitter = new EventEmitter(); } function addBid($bidderName, $bidAmount) { $this->eventEmitter->triggerEvent(''bid'', [$bidderName, $bidAmount]); } function addEventListener($eventName, callable $listener) { $this->eventEmitter->addEventListener($eventName, $listener); } function removeEventListener($eventName, callable $listener) { $this->eventEmitter->removeEventListener($eventName, $listener); } function removeAllEventListeners($eventName) { $this->eventEmitter->removeAllEventListeners($eventName); } }

Así es como se vería # 3 (rasgos):

trait EventEmitterTrait { private $eventListenersByName = []; function addEventListener($eventName, callable $listener) { $this->eventListenersByName[$eventName][] = $listener; } function removeEventListener($eventName, callable $listener) { $this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) { return $existingListener === $listener; }); } function removeAllEventListeners($eventName) { $this->eventListenersByName[$eventName] = []; } protected function triggerEvent($eventName, array $eventArgs) { foreach ($this->eventListenersByName[$eventName] as $listener) { call_user_func_array($listener, $eventArgs); } } } class Auction implements Observable { use EventEmitterTrait; function addBid($bidderName, $bidAmount) { $this->triggerEvent(''bid'', [$bidderName, $bidAmount]); } }

Tenga en cuenta que el código dentro de EventEmitterTrait es exactamente el mismo que está dentro de la clase EventEmitter , excepto que el rasgo declara que el método triggerEvent() protegido. Por lo tanto, la única diferencia que debe tener en cuenta es la implementación de la clase de Auction .

Y la diferencia es grande. Cuando usamos la composición, obtenemos una gran solución, que nos permite reutilizar nuestro EventEmitter por tantas clases como EventEmitter . Pero, el principal inconveniente es que tenemos una gran cantidad de código repetitivo que debemos escribir y mantener, ya que para cada método definido en la interfaz de Observable , necesitamos implementarlo y escribir código aburrido que solo envíe los argumentos al método correspondiente. en nuestro compuesto el objeto EventEmitter . El uso del rasgo en este ejemplo nos permite evitarlo , lo que nos ayuda a reducir el código de la caldera y mejorar el mantenimiento .

Sin embargo, puede haber ocasiones en las que no desee que su clase de Auction implemente la interfaz completa de Observable , tal vez solo desee exponer los métodos 1 o 2, o incluso ninguno, para que pueda definir sus propias firmas de métodos. En tal caso, es posible que prefiera el método de composición.

Pero, el rasgo es muy convincente en la mayoría de los escenarios, especialmente si la interfaz tiene muchos métodos, lo que hace que escribas un montón de repetitivo.

* En realidad, podrías hacer ambas cosas: define la clase EventEmitter en caso de que quieras usarla de forma compositiva y también define el rasgo EventEmitterTrait , usando la implementación de la clase EventEmitter dentro del rasgo :)


Para los principiantes, la respuesta puede ser difícil. Esta es la forma más fácil de entenderlo:

Rasgos

trait SayWorld { public function sayHello() { echo ''World!''; } }

así que si quieres tener la función sayHello en otras clases sin volver a crear toda la función, puedes usar rasgos,

class MyClass{ use SayWorld; } $o = new MyClass(); $o->sayHello();

¡Muy bien!

No solo funciones puedes usar cualquier cosa en el rasgo (función, variables, const ..). también puedes usar múltiples rasgos: use SayWorld,AnotherTraits;

Interfaz

interface SayWorld { public function sayHello(); } class MyClass implements SayWorld { public function sayHello() { echo ''World!''; } }

así es como la interfaz es diferente de las características: tiene que volver a crear todo en la interfaz en la clase implementada. La interfaz no tiene implementación. y la interfaz solo puede tener funciones y const, no puede tener variables.

¡Espero que esto ayude!


Si sabes inglés y sabes lo que significa trait , es exactamente lo que dice el nombre. Es un paquete sin clases de métodos y propiedades que se adjunta a las clases existentes al escribir use .

Básicamente, podrías compararlo con una sola variable. Las funciones de cierre pueden use estas variables desde fuera del alcance y de esa manera tienen el valor dentro. Son poderosos y pueden ser utilizados en todo. Lo mismo sucede con los rasgos si están siendo utilizados.


Un trait es esencialmente la implementación de una mixin PHP, y es efectivamente un conjunto de métodos de extensión que se pueden agregar a cualquier clase mediante la adición del trait . Los métodos luego se convierten en parte de la implementación de esa clase, pero sin utilizar la herencia .

Del Manual de PHP (énfasis mío):

Los rasgos son un mecanismo para la reutilización de código en lenguajes de herencia única como PHP. ... Es una adición a la herencia tradicional y permite la composición horizontal del comportamiento; es decir, la aplicación de los miembros de la clase sin necesidad de herencia.

Un ejemplo:

trait myTrait { function foo() { return "Foo!"; } function bar() { return "Bar!"; } }

Con el rasgo anterior definido, ahora puedo hacer lo siguiente:

class MyClass extends SomeBaseClass { use myTrait; // Inclusion of the trait myTrait }

En este punto, cuando creo una instancia de la clase MyClass , tiene dos métodos, llamados foo() y bar() , que provienen de myTrait . Y, observe que los métodos definidos por trait ya tienen un cuerpo de método, que un método definido por Interface no puede.

Además, PHP, como muchos otros idiomas, utiliza un modelo de herencia único , lo que significa que una clase puede derivar de múltiples interfaces, pero no de varias clases. Sin embargo, una clase de PHP puede tener múltiples inclusiones de trait , lo que le permite al programador incluir piezas reutilizables, como podría hacerlo si se incluyen varias clases base.

Algunas cosas a tener en cuenta:

----------------------------------------------- | Interface | Base Class | Trait | =============================================== > 1 per class | Yes | No | Yes | --------------------------------------------------------------------- Define Method Body | No | Yes | Yes | --------------------------------------------------------------------- Polymorphism | Yes | Yes | No | ---------------------------------------------------------------------

Polimorfismo:

En el ejemplo anterior, donde MyClass extiende SomeBaseClass , MyClass es una instancia de SomeBaseClass . En otras palabras, una matriz como SomeBaseClass[] bases puede contener instancias de MyClass . De manera similar, si MyClass extendió IBaseInterface , una matriz de IBaseInterface[] bases podría contener instancias de MyClass . No existe tal construcción polimórfica disponible con un trait , porque un trait es esencialmente un código que se copia para la conveniencia del programador en cada clase que lo usa.

Precedencia:

Como se describe en el Manual:

Un miembro heredado de una clase base es anulado por un miembro insertado por un Rasgo. El orden de prioridad es que los miembros de la clase actual sobrescriban los métodos de Rasgo, que a su vez anulan los métodos heredados.

Entonces, considere el siguiente escenario:

class BaseClass { function SomeMethod() { /* Do stuff here */ } } interface IBase { function SomeMethod(); } trait myTrait { function SomeMethod() { /* Do different stuff here */ } } class MyClass extends BaseClass implements IBase { use myTrait; function SomeMethod() { /* Do a third thing */ } }

Al crear una instancia de MyClass, arriba, ocurre lo siguiente:

  1. La Interface IBase requiere una función sin parámetros llamada SomeMethod() para ser proporcionada.
  2. La clase base BaseClass proporciona una implementación de este método, que satisface la necesidad.
  3. El trait myTrait proporciona una función sin parámetros llamada SomeMethod() , que tiene prioridad sobre la versión BaseClass
  4. La class MyClass proporciona su propia versión de SomeMethod() , que tiene prioridad sobre la versión del trait .

Conclusión

  1. Una Interface no puede proporcionar una implementación predeterminada de un cuerpo de método, mientras que un trait puede.
  2. Una Interface es un constructo polimórfico heredado , mientras que un trait no lo es.
  3. Se pueden usar Múltiples Interface en la misma clase, y también pueden usarse múltiples trait .

Una interfaz define un conjunto de métodos que la clase implementadora debe implementar.

Cuando se use un rasgo, también se implementan los métodos, lo que no ocurre en una Interface .

Esa es la mayor diferencia.

Desde la Reutilización Horizontal para PHP RFC :

Traits es un mecanismo para la reutilización de código en lenguajes de herencia única como PHP. Un rasgo tiene la intención de reducir algunas limitaciones de la herencia individual al permitir que un desarrollador reutilice conjuntos de métodos libremente en varias clases independientes que viven en jerarquías de clases diferentes.


Una interfaz es un contrato que dice "este objeto es capaz de hacer esto", mientras que un Rasgo le da al objeto la capacidad de hacerlo.

Un rasgo es esencialmente una forma de "copiar y pegar" el código entre clases.

intenta leer este articulo


Anuncio de servicio publico:

Quiero dejar constancia de que creo que los rasgos son casi siempre un olor a código y que deben evitarse en favor de la composición. Es mi opinión que la herencia individual se abusa frecuentemente hasta el punto de ser un antipatrón y la herencia múltiple solo agrava este problema. En la mayoría de los casos, le servirán mucho mejor si prefiere la composición sobre la herencia (sea simple o múltiple). Si todavía estás interesado en los rasgos y su relación con las interfaces, sigue leyendo ...

Vamos a empezar diciendo esto:

La programación orientada a objetos (POO) puede ser un paradigma difícil de comprender. El hecho de que esté utilizando clases no significa que su código esté orientado a objetos (OO).

Para escribir el código OO, debe comprender que la POO tiene que ver con las capacidades de sus objetos. Tienes que pensar en las clases en términos de lo que pueden hacer en lugar de lo que realmente hacen . Esto está en marcado contraste con la programación tradicional de procedimientos donde el enfoque está en hacer que un poco de código "haga algo".

Si el código OOP es sobre planificación y diseño, una interfaz es el plano y un objeto es la casa completamente construida. Mientras tanto, los rasgos son simplemente una forma de ayudar a construir la casa diseñada por el plan (la interfaz).

Interfaces

Entonces, ¿por qué deberíamos usar interfaces? Sencillamente, las interfaces hacen que nuestro código sea menos frágil. Si duda de esta afirmación, pregunte a cualquier persona que se haya visto obligada a mantener un código heredado que no haya sido escrito contra interfaces.

La interfaz es un contrato entre el programador y su código. La interfaz dice: "Mientras juegues según mis reglas, puedes implementarme como quieras y te prometo que no romperé tu otro código".

Entonces, como ejemplo, considere un escenario del mundo real (sin automóviles ni widgets):

Desea implementar un sistema de almacenamiento en caché para una aplicación web para reducir la carga del servidor

Usted comienza escribiendo una clase para almacenar en caché las respuestas usando APC:

class ApcCacher { public function fetch($key) { return apc_fetch($key); } public function store($key, $data) { return apc_store($key, $data); } public function delete($key) { return apc_delete($key); } }

Luego, en su objeto de respuesta http, comprueba si hay un acierto de caché antes de hacer todo el trabajo para generar la respuesta real:

class Controller { protected $req; protected $resp; protected $cacher; public function __construct(Request $req, Response $resp, ApcCacher $cacher=NULL) { $this->req = $req; $this->resp = $resp; $this->cacher = $cacher; $this->buildResponse(); } public function buildResponse() { if (NULL !== $this->cacher && $response = $this->cacher->fetch($this->req->uri()) { $this->resp = $response; } else { // build the response manually } } public function getResponse() { return $this->resp; } }

Este enfoque funciona muy bien. Pero quizás unas semanas después decida que desea usar un sistema de caché basado en archivos en lugar de APC. Ahora tiene que cambiar su código de controlador porque programó su controlador para que funcione con la funcionalidad de la clase ApcCacher lugar de con una interfaz que exprese las capacidades de la clase ApcCacher . Digamos que, en lugar de lo anterior, ha hecho que la clase Controller dependa de un CacherInterface lugar del ApcCacher concreto de la ApcCacher manera:

// your controller''s constructor using the interface as a dependency public function __construct(Request $req, Response $resp, CacherInterface $cacher=NULL)

Para estar de acuerdo con que usted define su interfaz como tal:

interface CacherInterface { public function fetch($key); public function store($key, $data); public function delete($key); }

A su vez, tiene su ApcCacher y sus nuevas clases de FileCacher implementan CacherInterface y programan su clase de Controller para usar las capacidades requeridas por la interfaz.

Este ejemplo (con suerte) demuestra cómo la programación a una interfaz le permite cambiar la implementación interna de sus clases sin preocuparse si los cambios romperán su otro código.

Rasgos

Los rasgos, por otro lado, son simplemente un método para reutilizar el código. Las interfaces no deben considerarse como una alternativa mutuamente excluyente a los rasgos. De hecho, la creación de rasgos que cumplan con las capacidades requeridas por una interfaz es el caso de uso ideal .

Solo debe usar rasgos cuando varias clases comparten la misma funcionalidad (probablemente dictada por la misma interfaz). No tiene sentido usar un rasgo para proporcionar funcionalidad para una sola clase: eso solo confunde lo que hace la clase y un mejor diseño movería la funcionalidad del rasgo a la clase relevante.

Considere la siguiente implementación del rasgo:

interface Person { public function greet(); public function eat($food); } trait EatingTrait { public function eat($food) { $this->putInMouth($food); } private function putInMouth($food) { // digest delicious food } } class NicePerson implements Person { use EatingTrait; public function greet() { echo ''Good day, good sir!''; } } class MeanPerson implements Person { use EatingTrait; public function greet() { echo ''Your mother was a hamster!''; } }

Un ejemplo más concreto: imagine que su FileCacher y su ApcCacher de la discusión de la interfaz utilizan el mismo método para determinar si una entrada de caché es obsoleta y debería eliminarse (obviamente, este no es el caso en la vida real, pero vaya con ella). Podría escribir un rasgo y permitir que ambas clases lo utilicen para el requisito de interfaz común.

Una última palabra de precaución: tenga cuidado de no exagerar con los rasgos. A menudo, los rasgos se utilizan como una muleta para un diseño deficiente cuando las implementaciones de clase únicas serían suficientes. Debe limitar los rasgos para cumplir con los requisitos de interfaz para el mejor diseño de código.


Los rasgos son simplemente para la reutilización del código .

La interfaz solo proporciona la firma de las funciones que se definirán en la clase en la que se puede usar según la discreción del programador . Dándonos así un prototipo para un grupo de clases .

Para referencia- http://www.php.net/manual/en/language.oop5.traits.php