¿Cuál es el punto de las interfaces en PHP?

224

Las interfaces le permiten crear código que define los métodos de las clases que lo implementan. Sin embargo, no puede agregar ningún código a esos métodos.

Las clases abstractas le permiten hacer lo mismo, junto con agregar código al método.

Ahora, si puedes lograr el mismo objetivo con clases abstractas, ¿por qué necesitamos el concepto de interfaces?

Me han dicho que tiene que ver con la teoría OO de C ++ a Java, que es en lo que se basa el material OO de PHP. ¿El concepto es útil en Java pero no en PHP? ¿Es solo una forma de evitar tener marcadores de posición en la clase abstracta? ¿Me estoy perdiendo de algo?

mk.
fuente
44
Tienes que leer esto: stackoverflow.com/a/384067/14673
Luc M
Estoy bastante seguro de que es una ayuda mental y una ayuda de comunicación. Las interfaces actúan como excelentes herramientas de enseñanza para su API, ya que agrupan los servicios que su API expone juntos de una manera abstracta que se puede leer sin conocer la implementación. Esto es costoso, pero como las personas que se sienten cómodas con la interfaz, ya pueden usar las funciones directamente sin necesidad de clases, ahorrando así un puntero. sin interfaces definidas por el lenguaje, puede ser complicado planear con anticipación para muchos programadores, ya que las personas acostumbradas a "usar lenguajes" a menudo prefieren diseñar con interfaces en lugar de hacerlo en papel.
Dmitry

Respuestas:

142

El objetivo de las interfaces es darle la flexibilidad de obligar a su clase a implementar múltiples interfaces, pero no permitir la herencia múltiple. Los problemas con la herencia de múltiples clases son muchos y variados y la página de Wikipedia en él los resume bastante bien.

Las interfaces son un compromiso. La mayoría de los problemas con la herencia múltiple no se aplican a las clases base abstractas, por lo que la mayoría de los lenguajes modernos actualmente deshabilitan la herencia múltiple pero llaman a las interfaces de las clases base abstractas y permiten que una clase "implemente" todas las que quieran.

Craig H
fuente
39
También se puede decir que * Las interfaces proporcionan el diseño para una clase con implementación cero. * Las clases abstractas proporcionan algo de diseño, con cierta implementación. Las clases abstractas son más útiles cuando las clases secundarias comparten algunas similitudes de implementación, pero difieren en ciertas implementaciones.
Jrgns
@ Craig, no hay ningún problema inherente con la herencia múltiple, solo que los idiomas actuales no son lo suficientemente potentes como para implementarlos correctamente. En realidad, los "problemas" pueden solucionarse con bastante facilidad, por ejemplo, establecer la ruta explícita de herencia para las funciones heredadas del mismo nombre puede resolver el dilema del diamante.
Pacerier
15
De eso no se trata en absoluto las interfaces. No es un compromiso sobre la herencia múltiple, se trata de crear un contrato conceptual y abstracto para que los objetos se implementen y para que otros objetos / métodos consuman. Las interfaces son una herramienta para el polimorfismo, y no para la herencia directa.
El fantasma de Madara
123

El concepto es útil en la programación orientada a objetos. Para mí, pienso en una interfaz como un contrato. Mientras mi clase y su clase estén de acuerdo con este método de firma de contrato, podemos "interactuar". En cuanto a las clases abstractas, las veo como más clases básicas que resuelven algunos métodos y necesito completar los detalles.

John Downey
fuente
Esto me hizo entenderlo un poco. Es como con los espacios de nombres, por lo que el código compartido es más fácil de usar y sin conflictos. Es más fácil cuando dos personas hacen clases en la misma base, ¿verdad?
RedClover
¿No necesitamos distinguir el concepto general de una 'interfaz' de las interfaces concretas en un lenguaje como PHP? Cualquier función, por ejemplo, tiene una "interfaz" que define cómo se usa y oculta su implementación. Por lo tanto, ese tipo de interfaz "contractual" no requiere una función de idioma especial. Por lo tanto, la función de idioma debe ser para otra cosa (o algo adicional).,
UuDdLrLrSs
70

¿Por qué necesitarías una interfaz, si ya hay clases abstractas? Para evitar la herencia múltiple (puede causar múltiples problemas conocidos).

Uno de esos problemas:

El "problema del diamante" (a veces denominado "diamante mortal de la muerte") es una ambigüedad que surge cuando dos clases B y C heredan de A y la clase D hereda de B y C. Si hay un método en A que B y C se han anulado, y D no lo anula, entonces, ¿qué versión del método hereda D: la de B o la de C?

Fuente: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

¿Por qué / cuándo usar una interfaz? Un ejemplo ... Todos los autos en el mundo tienen la misma interfaz (métodos) ... AccelerationPedalIsOnTheRight(), BrakePedalISOnTheLeft(). Imagine que cada marca de automóvil tendría estos "métodos" diferentes de otra marca. BMW tendría los frenos en el lado derecho, y Honda tendría frenos en el lado izquierdo del volante. Las personas tendrían que aprender cómo funcionan estos "métodos" cada vez que comprarían una marca de automóviles diferente. Por eso es una buena idea tener la misma interfaz en múltiples "lugares".

¿Qué hace una interfaz por usted (por qué alguien usaría una)? Una interfaz le impide cometer "errores" (le asegura que todas las clases que implementan una interfaz específica tendrán todos los métodos que están en la interfaz).

// Methods inside this interface must be implemented in all classes which implement this interface.
interface IPersonService
{   
    public function Create($personObject);
}

class MySqlPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Create a new person in MySql database.
    }
}

class MongoPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Mongo database creates a new person differently then MySQL does. But the code outside of this method doesn't care how a person will be added to the database, all it has to know is that the method Create() has 1 parameter (the person object).
    }
}

De esta manera, el Create()método siempre se utilizará de la misma manera. No importa si estamos usando la MySqlPersonclase o la MongoPersonclase. La forma en que estamos utilizando un método permanece igual (la interfaz sigue siendo la misma).

Por ejemplo, se usará así (en todas partes en nuestro código):

new MySqlPerson()->Create($personObject);
new MongoPerson()->Create($personObject);

De esta manera, algo como esto no puede suceder:

new MySqlPerson()->Create($personObject)
new MongoPerson()->Create($personsName, $personsAge);

Es mucho más fácil recordar una interfaz y usar la misma en todas partes, que múltiples diferentes.

De esta manera, el interior del Create()método puede ser diferente para diferentes clases, sin afectar el código "externo", que llama a este método. Todo lo que el código externo tiene que saber es que el método Create()tiene 1 parámetro ( $personObject), porque así es como el código externo usará / llamará al método. Al código externo no le importa lo que sucede dentro del método; solo tiene que saber cómo usarlo / llamarlo.

También puede hacer esto sin una interfaz, pero si usa una interfaz, es "más segura" (porque evita que cometa errores). La interfaz le asegura que el método Create()tendrá la misma firma (los mismos tipos y el mismo número de parámetros) en todas las clases que implementan la interfaz. De esta manera, puede estar seguro de que CUALQUIER clase que implemente la IPersonServiceinterfaz tendrá el método Create()(en este ejemplo) y solo necesitará 1 parámetro ( $personObject) para ser llamado / utilizado.

Una clase que implementa una interfaz debe implementar todos los métodos, que la interfaz hace / tiene.

Espero no haberme repetido demasiado.

Jo Smo
fuente
Muy buena analogía con los pedales del automóvil.
James
24

La diferencia entre usar una interfaz y una clase abstracta tiene más que ver con la organización del código para mí que con la aplicación del lenguaje en sí. Los uso mucho cuando preparo código para que otros desarrolladores trabajen para que se mantengan dentro de los patrones de diseño previstos. Las interfaces son un tipo de "diseño por contrato" por el cual su código acepta responder a un conjunto prescrito de llamadas API que pueden provenir de un código que no tiene acceso.

Si bien la herencia de la clase abstracta es una relación "es un", eso no siempre es lo que desea, e implementar una interfaz es más una relación "actúa como una". Esta diferencia puede ser bastante significativa en ciertos contextos.

Por ejemplo, supongamos que tiene una cuenta de clase abstracta desde la que se extienden muchas otras clases (tipos de cuentas, etc.). Tiene un conjunto particular de métodos que solo son aplicables a ese grupo de tipos. Sin embargo, algunas de estas subclases de cuentas implementan Versionable, Listable o Editable para que puedan ser lanzadas a controladores que esperan usar esas API. Al controlador no le importa qué tipo de objeto es

Por el contrario, también puedo crear un objeto que no se extienda desde la Cuenta, digamos una clase abstracta de Usuario, y aún implementar Listable y Editable, pero no Versátil, lo que no tiene sentido aquí.

De esta manera, estoy diciendo que la subclase de FooUser NO es una cuenta, pero sí actúa como un objeto editable. Del mismo modo, BarAccount se extiende desde la Cuenta, pero no es una subclase de Usuario, sino que implementa Editable, Listable y Versionable.

Agregar todas estas API para Editable, Listable y Versionable en las clases abstractas en sí mismas no solo sería desordenado y feo, sino que duplicaría las interfaces comunes en Account and User, o forzaría a mi objeto User a implementar Versionable, probablemente solo para lanzar un excepción.

Sam McAfee
fuente
Esto es justo aquí. Aplica rigurosamente a los desarrolladores para que usen tus métodos sin extenderlos ni sobrescribirlos
Nick el
21

Las interfaces son esencialmente un plan para lo que puede crear. Definen qué métodos debe tener una clase , pero puede crear métodos adicionales fuera de esas limitaciones.

No estoy seguro de lo que quieres decir con no poder agregar código a los métodos, porque puedes hacerlo. ¿Estás aplicando la interfaz a una clase abstracta o la clase que la extiende?

Será necesario implementar un método en la interfaz aplicada a la clase abstracta en esa clase abstracta. Sin embargo, aplique esa interfaz a la clase extendida y el método solo necesita implementarse en la clase extendida. Podría estar equivocado aquí: no uso interfaces tan a menudo como podría / debería.

Siempre he pensado en las interfaces como un patrón para desarrolladores externos o un conjunto de reglas adicional para garantizar que las cosas sean correctas.

Ross
fuente
99
En PHP, las interfaces contienen solo la declaración del método y no la implementación real. Sin embargo, las clases abstractas le permiten "agregar código" a los métodos que heredarán las clases que lo extienden. Creo que esta diferencia es a lo que se refería mk.
nocash
13

Utilizará interfaces en PHP:

  1. Para ocultar la implementación: establezca un protocolo de acceso a una clase de objetos y cambie la implementación subyacente sin refactorizar en todos los lugares donde ha utilizado esos objetos
  2. Para verificar el tipo, como para asegurarse de que un parámetro tenga un tipo específico $object instanceof MyInterface
  3. Para hacer cumplir la comprobación de parámetros en tiempo de ejecución
  4. Para implementar múltiples comportamientos en una sola clase (compilar tipos complejos)

    El coche de clase implementa EngineInterface, BodyInterface, SteeringInterface {

para que un Carobjeto pueda ahora start(), stop()(EngineInterface) o goRight(), goLeft()(Interfaz de dirección)

y otras cosas que no puedo pensar en este momento

Número 4 es probablemente el caso de uso más obvio que no puede abordar con clases abstractas.

De Pensar en Java:

Una interfaz dice: "Así es como se verán todas las clases que implementan esta interfaz en particular". Por lo tanto, cualquier código que use una interfaz particular sabe qué métodos se pueden llamar para esa interfaz, y eso es todo. Entonces, la interfaz se utiliza para establecer un "protocolo" entre clases.

catalin.costache
fuente
10

Las interfaces existen no como una base sobre la cual las clases pueden extenderse sino como un mapa de funciones requeridas.

El siguiente es un ejemplo de uso de una interfaz donde una clase abstracta no cabe:
Digamos que tengo una aplicación de calendario que permite a los usuarios importar datos de calendario de fuentes externas. Escribiría clases para manejar la importación de cada tipo de fuente de datos (ical, rss, atom, json) Cada una de esas clases implementaría una interfaz común que garantizaría que todos tengan los métodos públicos comunes que mi aplicación necesita para obtener los datos.

<?php

interface ImportableFeed 
{
    public function getEvents();
}

Luego, cuando un usuario agrega un nuevo feed, puedo identificar el tipo de feed que es y usar la clase desarrollada para ese tipo para importar los datos. Cada clase escrita para importar datos para un feed específico tendría un código completamente diferente, de lo contrario puede haber muy pocas similitudes entre las clases, aparte del hecho de que están obligados a implementar la interfaz que permite que mi aplicación los consuma. Si tuviera que usar una clase abstracta, podría ignorar fácilmente el hecho de que no he anulado el método getEvents () que luego rompería mi aplicación en este caso, mientras que usar una interfaz no permitiría que mi aplicación se ejecute si CUALQUIERA de los métodos definido en la interfaz no existe en la clase que lo implementó. Mi aplicación no tiene que preocuparse de qué clase usa para obtener datos de un feed,

Para llevar esto un paso más allá, la interfaz demuestra ser extremadamente útil cuando vuelvo a mi aplicación de calendario con la intención de agregar otro tipo de feed. El uso de la interfaz ImportableFeed significa que puedo continuar agregando más clases que importen diferentes tipos de feed simplemente agregando nuevas clases que implementen esta interfaz. Esto me permite agregar toneladas de funcionalidad sin tener que agregar un volumen innecesario a mi aplicación principal, ya que mi aplicación principal solo se basa en que existan los métodos públicos disponibles que la interfaz requiere, siempre que mis nuevas clases de importación de feeds implementen la interfaz ImportableFeed. Sé que puedo dejarlo en su lugar y seguir moviéndome.

Este es solo un comienzo muy simple. Luego puedo crear otra interfaz que se requiera que implementen todas mis clases de calendario que ofrezca más funcionalidad específica para el tipo de feed que maneja la clase. Otro buen ejemplo sería un método para verificar el tipo de alimentación, etc.

Esto va más allá de la pregunta, pero dado que utilicé el ejemplo anterior: las interfaces vienen con su propio conjunto de problemas si se usan de esta manera. Creo que necesito asegurar la salida que se devuelve de los métodos implementados para que coincida con la interfaz y para lograr esto, uso un IDE que lee bloques PHPDoc y agrego el tipo de retorno como una pista de tipo en un bloque PHPDoc de la interfaz que luego traducir a la clase concreta que lo implementa. Mis clases que consuman la salida de datos de las clases que implementan esta interfaz sabrán, al menos, que espera una matriz devuelta en este ejemplo:

<?php
interface ImportableFeed 
{
    /**
     * @return array
     */
    public function getEvents();
}

No hay mucho espacio para comparar clases abstractas e interfaces. Las interfaces son simplemente mapas que, cuando se implementan, requieren que la clase tenga un conjunto de interfaces públicas.

Houghtelin
fuente
Buena explicación :) Gracias!
Razvan.432
Solo para agregar información: en la clase abstracta, si declara el método como abstracto, se comporta como una interfaz, por lo que no puede ignorar el hecho de que no anuló getEvents (). La aplicación fallará de la misma manera que con la interfaz.
Rikudou_Sennin
8

Las interfaces no son solo para asegurarse de que los desarrolladores implementen ciertos métodos. La idea es que debido a que estas clases tienen ciertos métodos garantizados, puede usar estos métodos incluso si no conoce el tipo real de la clase. Ejemplo:

interface Readable {
  String read();
}

List<Readable> readables; // dunno what these actually are, but we know they have read();
for(Readable reader : readables)
  System.out.println(reader.read());

En muchos casos, no tiene sentido proporcionar una clase base, abstracta o no, porque las implementaciones varían enormemente y no comparten nada en común además de algunos métodos.

Los lenguajes escritos dinámicamente tienen la noción de "tipear patos" donde no se necesitan interfaces; puede suponer que el objeto tiene el método que está solicitando. Esto soluciona el problema en lenguajes estáticamente escritos donde su objeto tiene algún método (en mi ejemplo, read ()), pero no implementa la interfaz.

Programador ilegal
fuente
6

En mi opinión, las interfaces deberían preferirse a las clases abstractas no funcionales. No me sorprendería si hubiera incluso un golpe de rendimiento allí, ya que solo hay un objeto instanciado, en lugar de analizar dos, combinándolos (aunque, no estoy seguro, no estoy familiarizado con el funcionamiento interno de OOP PHP).

Es cierto que las interfaces son menos útiles / significativas que en comparación con, por ejemplo, Java. Por otro lado, PHP6 introducirá aún más sugerencias de tipo, incluida la sugerencia de tipo para los valores de retorno. Esto debería agregar algo de valor a las interfaces PHP.

tl; dr: interfaces define una lista de métodos que deben seguirse (piense en la API), mientras que una clase abstracta proporciona algunas funciones básicas / comunes, que las subclases refinan para necesidades específicas.

Henrik Paul
fuente
PHP 6 nunca será lanzado. PHP 6 fue un proyecto que estaba en desarrollo entre 2005 y 2010, pero se retrasó y finalmente se canceló. PHP 7 es la próxima versión, principalmente para evitar confusiones con el antiguo proyecto PHP 6.
Ashu Jha
5

No puedo recordar si PHP es diferente a este respecto, pero en Java, puede implementar múltiples interfaces, pero no puede heredar múltiples clases abstractas. Supongo que PHP funciona de la misma manera.

En PHP, puede aplicar varias interfaces separándolas con una coma (creo que no encuentro una solución limpia).

En cuanto a las múltiples clases abstractas, podría tener varios resúmenes que se extiendan entre sí (nuevamente, no estoy totalmente seguro de eso, pero creo que lo he visto en algún lugar antes). Lo único que no puedes extender es una clase final.

Ross
fuente
4

Las interfaces no le darán a su código ningún aumento en el rendimiento ni nada de eso, pero pueden hacer mucho para que sea mantenible. Es cierto que se puede usar una clase abstracta (o incluso una clase no abstracta) para establecer una interfaz para su código, pero las interfaces adecuadas (las que define con la palabra clave y que solo contienen firmas de métodos) son simplemente más fáciles de usar. ordenar y leer.

Dicho esto, tiendo a usar la discreción al decidir si usar o no una interfaz en una clase. A veces quiero implementaciones de métodos por defecto, o variables que serán comunes a todas las subclases.

Por supuesto, el punto sobre la implementación de múltiples interfaces también es sólido. Si tiene una clase que implementa múltiples interfaces, puede usar un objeto de esa clase como diferentes tipos en la misma aplicación.

Sin embargo, el hecho de que su pregunta sea sobre PHP hace que las cosas sean un poco más interesantes. Escribir en interfaces todavía no es increíblemente necesario en PHP, donde puedes alimentar prácticamente cualquier cosa a cualquier método, independientemente de su tipo. Puede escribir parámetros de método de forma estática, pero parte de eso está roto (String, creo, causa algunos problemas). Combine esto con el hecho de que no puede escribir la mayoría de las otras referencias, y no hay mucho valor en tratar de forzar la escritura estática en PHP ( en este punto ). Y debido a eso, el valor de las interfaces en PHP , en este puntoes mucho menos de lo que está en los idiomas más fuertemente tipados. Tienen el beneficio de la legibilidad, pero poco más. La implementación múltiple ni siquiera es beneficiosa, porque aún tiene que declarar los métodos y darles cuerpos dentro del implementador.

Brian Warshaw
fuente
2

Debajo están los puntos para la interfaz PHP

  1. Se utiliza para definir el número requerido de métodos en la clase [si desea cargar html, se requiere id y nombre, por lo que en este caso la interfaz incluye setID y setName].
  2. La interfaz obliga estrictamente a la clase a incluir todos los métodos definidos en ella.
  3. Solo puede definir el método en la interfaz con accesibilidad pública.
  4. También puede extender la interfaz como clase. Puede extender la interfaz en php usando la palabra clave extend.
  5. Extiende la interfaz múltiple.
  6. No puede implementar 2 interfaces si ambas comparten la función con el mismo nombre. Lanzará un error.

Código de ejemplo:

interface test{
    public function A($i);
    public function B($j = 20);
}

class xyz implements test{
    public function A($a){
        echo "CLASS A Value is ".$a;
    }
    public function B($b){
        echo "CLASS B Value is ".$b;
    }
}
$x = new xyz();
echo $x->A(11);
echo "<br/>";
echo $x->B(10);
vivek s vamja
fuente
2

Vimos que las clases abstractas y las interfaces son similares en el sentido de que proporcionan métodos abstractos que deben implementarse en las clases secundarias. Sin embargo, todavía tienen las siguientes diferencias:

1. Las interfaces pueden incluir métodos abstractos y constantes, pero no pueden contener métodos y variables concretas.

2. Todos los métodos en la interfaz deben estar dentro del alcance de la visibilidad pública .

3. Una clase puede implementar más de una interfaz, mientras que puede heredar de una sola clase abstracta.

                                  interface                      abstract class
the code                     - abstract methods               - abstract methods
                             - constants                      - constants                  
                                                              - concrete methods
                                                              - concrete variables

access modifiers             
                             - public                         - public
                                                              - protected
                                                              - private
                                                                etc.
number of parents          The same class can implement
                           more than 1 interface              The child class can 
                                                              inherit only from 1 abstract class

Espero que esto ayude a cualquiera a entender!

Hiren Gohel
fuente
2

Las interfaces son como tus genes.

Las clases abstractas son como tus padres reales.

Sus propósitos son hereditarios, pero en el caso de las clases abstractas frente a las interfaces, lo que se hereda es más específico.

Tapha
fuente
2

No sé sobre otros idiomas, cuál es el concepto de interfaz allí. Pero para PHP, haré todo lo posible para explicarlo. Solo sea paciente y comente si esto ayudó.

Una interfaz funciona como un "contrato", especificando lo que hace un conjunto de subclases, pero no cómo lo hacen.

La regla

  1. Una interfaz no puede ser instanciada.

  2. No puede implementar ningún método en una interfaz, es decir, solo contiene la firma del método pero no los detalles (cuerpo).

  3. Las interfaces pueden contener métodos y / o constantes, pero no atributos. Las constantes de interfaz tienen las mismas restricciones que las constantes de clase. Los métodos de interfaz son implícitamente abstractos.

  4. Las interfaces no deben declarar constructores o destructores, ya que estos son detalles de implementación en el nivel de clase.

  5. Todos los métodos en una interfaz deben tener visibilidad pública.

Ahora tomemos un ejemplo. Supongamos que tenemos dos juguetes: uno es un perro y otro es un gato.

Como sabemos, un perro ladra y un gato maúlla. Estos dos tienen el mismo método de habla, pero con diferente funcionalidad o implementación. Supongamos que le estamos dando al usuario un control remoto que tiene un botón para hablar.

Cuando el usuario presiona el botón de hablar, el juguete tiene que hablar, no importa si es un perro o un gato.

Este es un buen caso para usar una interfaz, no una clase abstracta porque las implementaciones son diferentes. ¿Por qué? Recuerda

Si necesita admitir las clases secundarias agregando algún método no abstracto, debe usar clases abstractas. De lo contrario, las interfaces serían su elección.

Comunidad
fuente