Interfaz o una clase abstracta: ¿cuál usar?

327

Por favor explique cuándo debo usar un PHP interfacey cuándo debo usar un abstract class?

¿Cómo puedo cambiar mi entrada abstract classa una interface?

Darryl Hein
fuente

Respuestas:

458

Use una interfaz cuando quiera forzar a los desarrolladores que trabajan en su sistema (incluido usted mismo) para implementar un número determinado de métodos en las clases que van a construir.

Utilice una clase abstracta cuando desee forzar a los desarrolladores que trabajan en su sistema (incluido usted mismo) para implementar un número determinado de métodos y desee proporcionar algunos métodos básicos que los ayuden a desarrollar sus clases secundarias.

Otra cosa a tener en cuenta es que las clases de cliente solo pueden extender una clase abstracta, mientras que pueden implementar múltiples interfaces. Entonces, si está definiendo sus contratos de comportamiento en clases abstractas, eso significa que cada clase secundaria solo puede conformarse a un solo contrato. A veces esto es bueno, cuando quieres forzar a tus programadores de usuarios a seguir un camino en particular. Otras veces sería malo. Imagínese si las interfaces Contable e Iterador de PHP fueran clases abstractas en lugar de interfaces.

Un enfoque que es común cuando no está seguro de qué camino tomar (como se menciona en cletus a continuación ) es crear una interfaz y luego hacer que su clase abstracta implemente esa interfaz.

Alan Storm
fuente
12
Intenté todo el día comprender los usos abstracty las interfaceclases, tu publicación lo dejó todo claro. Muchas gracias Alan
afarazit
44
Otra ventaja de las clases abstractas es la capacidad de definir métodos abstractos protegidos . No siempre es útil, pero puede ser útil en algunas arquitecturas.
netcoder
Entonces, en muchos casos, deberíamos usar la clase abstracta debido a la flexibilidad, esa es mi conclusión :)
ymakux
3
@volocuga: no necesariamente, como señaló Alan, solo se puede extender un solo resumen. Personalmente, no me gusta que el resumen implemente una idea de interfaz porque contribuye a la ofuscación del código y es menos directo, en mi opinión.
Prefijo
171

Las diferencias entre an Abstract Classy an Interface:

Clases abstractas

Una clase abstracta puede proporcionar alguna funcionalidad y dejar el resto para la clase derivada .

  • La clase derivada puede o no anular las funciones concretas definidas en la clase base.

  • Una clase secundaria extendida desde una clase abstracta debería estar relacionada lógicamente.

Interfaz

Una interfaz no puede contener ninguna funcionalidad . Es solamente contiene las definiciones de los métodos.

  • La clase derivada DEBE proporcionar código para todos los métodos definidos en la interfaz .

  • Las clases completamente diferentes y no relacionadas pueden agruparse lógicamente mediante una interfaz.

kn3l
fuente
1
¿Puedes dar un ejemplo de la vida real para demostrarlo?
RN Kushwaha
1
¿Cuál es la diferencia entre abstract class X implements Yy class X implements Y?
Webinan
3
@Webinan In abstract class X implements Ydeclara que la funcionalidad masiva de X debe implementarse en una clase derivada y que tanto la clase abstracta como la derivada deben contener funciones definidas en Y, mientras que class X implements Ysolo implica que la clase X debe contener funciones definidas en Y. Si su interfaz Y no está destinado a ser implementado por ninguna otra clase que XI realmente omitiría la definición de Y como interfaz y solo implementaría las funciones en Y como función abstracta pública / protegida / privada para asegurarse de que se implementen en la clase derivada.
Calle Bergström
1
Las interfaces no solo pueden contener la definición de métodos, sino que también pueden contener constantes
Thielicious
Me gustó tu comparación. Así que quería agregar algo. Las interfaces pueden tener constantes de clase listas para usar, mientras que la clase abstracta no.
Noman Ibrahim
128

¿Por qué usar clases abstractas? El siguiente es un ejemplo simple. Digamos que tenemos el siguiente código:

<?php 

class Fruit {
    private $color;

    public function eat() {
        // chew
    }

    public function setColor($c) {
        $this->color = $c;
    }
}

class Apple extends Fruit {
    public function eat() {
        // chew until core
    }
}

class Orange extends Fruit {
    public function eat() {
        // peeling
        // chew
    }
}

Ahora te doy una manzana y te la comes. A qué sabe esto? Sabe a manzana.

<?php 
$apple = new Apple();
$apple->eat();

// Now I give you a fruit.
$fruit = new Fruit();
$fruit->eat();

¿A qué sabe eso? Bueno, no tiene mucho sentido, así que no deberías poder hacerlo. Esto se logra haciendo que la clase Fruit sea abstracta, así como el método de comer dentro de ella.

<?php 
abstract class Fruit {
    private $color;

    abstract public function eat(){}

    public function setColor($c) {
        $this->color = $c;
    }
}
?>

Una clase abstracta es como una interfaz, pero puede definir métodos en una clase abstracta mientras que en una interfaz todos son abstractos. Las clases abstractas pueden tener métodos vacíos y de trabajo / concretos. En las interfaces, las funciones definidas allí no pueden tener un cuerpo. En clases abstractas, pueden.

Un ejemplo del mundo real:

<?php 
abstract class person {

    public $LastName;
    public $FirstName;
    public $BirthDate;

    abstract protected function write_info();
}

final class employee extends person{

    public $EmployeeNumber;
    public $DateHired;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to emloyee dbase table <br>";   
    }
}

final class student extends person{

    public $StudentNumber;
    public $CourseName;

    public function write_info(){
        //sql codes here
        echo "Writing ". $this->LastName . "'s info to student dbase table <br>";
    }
}

///----------
$personA = new employee;
$personB = new student;

$personA->FirstName="Joe";
$personA->LastName="Sbody";

$personB->FirstName="Ben";
$personB->LastName="Dover";

$personA->write_info();
// Writing Sbody's info to emloyee dbase table
$personB->write_info();
// Writing Dover's info to student dbase table 
Vineesh Kalarickal
fuente
2
Hola, esta respuesta probablemente recibió votos negativos debido a la forma en que está formateada. Sería bueno si no fuera un bloque de código grande (cuatro espacios convierten algo en un bloque de código, desangra el texto para sacarlo del bloque), y si esto fue copiado desde algún lugar (parece que sí) Sería educado darles crédito.
Camilo Martin
99
Te amo por la fruta ejemplo hombre! Desde que comencé a aprender php, esto me deja en claro muchas gracias
Raheel
23
+1 ¡ What does that taste like? Well, it doesn't make much sense, so you shouldn't be able to do that.Ahora sé abstracto!
Webinan
¿Qué hace la finalpalabra clave? Gran post, gracias.
Gus
1
@VineeshKalarickal Lo que no entiendo en el ejemplo de Persona es la diferencia entre: 1) usar la clase abstracta Persona (como en el ejemplo); 2) escribir Person como una clase estándar y hacer que Employee y Student anulen el método write_info ().
Ferex
66

La mejor práctica es utilizar una interfaz para especificar el contrato y una clase abstracta como solo una implementación del mismo. Esa clase abstracta puede completar una gran cantidad de repeticiones para que pueda crear una implementación simplemente anulando lo que necesita o desea sin obligarlo a usar una implementación particular.

cletus
fuente
37

Solo para incluir esto en la mezcla, pero como Cletus mencionó usar una interfaz junto con una clase abstracta, a menudo uso la interfaz para aclarar mi pensamiento de diseño.

Por ejemplo:

<?php
class parser implements parserDecoratorPattern {
    //...
}

De esa manera, cualquiera que lea mi código (y quién sabe qué es un Patrón de decorador) sabrá de inmediato a) cómo construyo mi analizador yb) podrá ver qué métodos se utilizan para implementar el patrón de decorador.

Además, y puedo estar fuera de la base aquí no siendo un programador de Java / C ++ / etc., pero los tipos de datos pueden entrar en juego aquí. Sus objetos son de un tipo, y cuando los pasa, el tipo importa mediante programación. Mover sus elementos contraíbles a la interfaz solo dicta los tipos que devuelven los métodos, pero no el tipo base de la clase que lo implementa.

Es tarde y no puedo pensar en un mejor ejemplo de psudocódigo, pero aquí va:

<?php
interface TelevisionControls {};
class Remote implements TelevisionControls {};
class Spouse implements TelevisionControls {};
Spouse spouse = new Spouse();
Remote remote = new Remote();
isSameType = (bool)(remote == spouse)
Austen Hoogen
fuente
1
¡Qué ejemplo tan maravilloso! ;)
Joel Murphy
@ matt2000 No es sexista Y ahora también trabaja para matrimonios del mismo sexo. Gran edición :)
Austen Hoogen
44
¡Este es un ejemplo increíble! Sin embargo, no creo que pueda crear una instancia de una clase abstracta, sino más bien una clase que se extiende desde una clase abstracta
Arielle Nguyen
2
no mataría para hacer que el código sudo al menos se parezca al idioma en cuestión. alguien debería poner algunos $ 's allí.
Luché con un oso una vez.
2
Si bien el ejemplo es divertido, no podemos crear una instancia de una clase abstracta directamente, realice cambios en el ejemplo para que las personas no consideren que es un uso válido en PHP.
saji89
16

La principal diferencia es que una clase abstracta puede contener una implementación predeterminada, mientras que una interfaz no puede.

Una interfaz es un contrato de comportamiento sin ninguna implementación.

Trigo Mitch
fuente
16

Además, me gustaría agregar aquí que el hecho de que cualquier otro lenguaje OO tenga algún tipo de interfaz y abstracción también no significa que tengan el mismo significado y propósito que en PHP. El uso de abstracción / interfaces es ligeramente diferente, mientras que las interfaces en PHP en realidad no tienen una función real. Simplemente se utilizan por razones semánticas y relacionadas con el esquema. El punto es tener un proyecto lo más flexible posible, ampliable y seguro para futuras extensiones, independientemente de si el desarrollador tiene un plan de uso totalmente diferente o no.

Si su inglés no es nativo, puede buscar lo que realmente son abstracción e interfaces. Y busque sinónimos también.

Y esto podría ayudarte como metáfora:

INTERFAZ

Digamos que horneas un nuevo tipo de pastel con fresas y preparas una receta que describe los ingredientes y los pasos. Solo usted sabe por qué sabe tan bien y a sus invitados les gusta. Luego decides publicar tu receta para que otras personas puedan probar ese pastel también.

El punto aquí es

- para hacer las cosas bien
- para tener cuidado
- para evitar cosas que podrían salir mal (como demasiadas fresas o algo así)
- para que sea fácil para las personas que lo prueban
- para decirle cuánto tiempo es qué hacer (como agitar )
- para decir qué cosas PUEDE hacer pero NO DEBE

Exactamente ESTO es lo que describe las interfaces. Es una guía, un conjunto de instrucciones que observan el contenido de la receta. Lo mismo que crearía un proyecto en PHP y desea proporcionar el código en GitHub o con sus compañeros o lo que sea. Una interfaz es lo que la gente puede hacer y lo que usted no debería hacer. Reglas que lo sostienen: si desobedeces una, la construcción completa se romperá.


ABSTRACCIÓN

Para continuar con esta metáfora aquí ... imagínese, usted es el invitado esta vez comiendo ese pastel. Entonces estás probando ese pastel usando la receta ahora. Pero desea agregar nuevos ingredientes o cambiar / omitir los pasos descritos en la receta. Entonces, ¿qué viene después? Planea una versión diferente de ese pastel. Esta vez con bayas negras y no bayas de paja y más crema de vainilla ... delicioso.

Esto es lo que podría considerar una extensión del pastel original. Básicamente haces una abstracción al crear una nueva receta porque es muy diferente. Tiene algunos pasos nuevos y otros ingredientes. Sin embargo, la versión de baya negra tiene algunas partes que tomó del original: estos son los pasos básicos que debe tener todo tipo de pastel. Como ingredientes como la leche: eso es lo que tiene cada clase derivada.

Ahora desea intercambiar ingredientes y pasos, y estos DEBEN definirse en la nueva versión de ese pastel. Estos son métodos abstractos que deben definirse para el nuevo pastel, porque debería haber una fruta en el pastel, pero ¿cuál? Entonces tomas las bayas negras esta vez. Hecho.

Ahí tienes, has extendido el pastel, seguido la interfaz y los pasos e ingredientes abstraídos de él.

Thielicious
fuente
1
Esta ha sido mi comparación favorita de todo lo relacionado con PHP. Realmente tenía sentido. ¡Gracias!
cbloss793
13

Para agregar a algunas de las respuestas ya excelentes:

  • Las clases abstractas le permiten proporcionar cierto grado de implementación, las interfaces son plantillas puras. Una interfaz solo puede definir la funcionalidad , nunca puede implementarla.

  • Cualquier clase que implemente la interfaz se compromete a implementar todos los métodos que defina o debe declararse abstracta.

  • Las interfaces pueden ayudar a gestionar el hecho de que, como Java, PHP no admite la herencia múltiple. Una clase PHP solo puede extender a un solo padre. Sin embargo, puede hacer una promesa de clase para implementar tantas interfaces como desee.

  • tipo: para cada interfaz que implementa, la clase toma el tipo correspondiente. Debido a que cualquier clase puede implementar una interfaz (o más interfaces), las interfaces efectivamente unen tipos que de otro modo no están relacionados.

  • una clase puede extender una superclase e implementar cualquier cantidad de interfaces:

    class SubClass extends ParentClass implements Interface1, Interface2 {
        // ...
    }

¿Explica cuándo debo usar una interfaz y cuándo debo usar la clase abstracta?

Use una interfaz cuando necesite proporcionar solo una plantilla sin implementación alguna y quiera asegurarse de que cualquier clase que implemente esa interfaz tenga los mismos métodos que cualquier otra clase que la implemente (al menos).

Use una clase abstracta cuando desee crear una base para otros objetos (una clase parcialmente construida). La clase que extiende su clase abstracta usará algunas propiedades o métodos definidos / implementados:

<?php
// interface
class X implements Y { } // this is saying that "X" agrees to speak language "Y" with your code.

// abstract class
class X extends Y { } // this is saying that "X" is going to complete the partial class "Y".
?>

¿Cómo puedo cambiar mi clase abstracta a una interfaz?

Aquí hay un caso / ejemplo simplificado. Saque todos los detalles de implementación. Por ejemplo, cambie su clase abstracta de:

abstract class ClassToBuildUpon {
    public function doSomething() {
          echo 'Did something.';
    }
}

a:

interface ClassToBuildUpon {
    public function doSomething();
}
bg17aw
fuente
12

Desde un punto de vista filosófico:

  • Una clase abstracta representa una relación "es una". Digamos que tengo frutas, bueno, tendría una clase de resumen de frutas que comparte responsabilidades y comportamientos comunes.

  • Una interfaz representa una relación "debería hacerlo". Una interfaz, en mi opinión (que es la opinión de un desarrollador junior), debe ser nombrada por una acción, o algo parecido a una acción, (Lo siento, no puedo encontrar la palabra, no soy un hablante nativo de inglés) Digamos IEatable. Sabes que se puede comer, pero no sabes lo que comes.

Desde un punto de vista de codificación:

  • Si sus objetos tienen código duplicado, es una indicación de que tienen un comportamiento común, lo que significa que podría necesitar una clase abstracta para reutilizar el código, lo que no puede hacer con una interfaz.

  • Otra diferencia es que un objeto puede implementar tantas interfaces como necesite, pero solo puede tener una clase abstracta debido al "problema del diamante" (¡consulte aquí para saber por qué! Http://en.wikipedia.org/wiki/ Multiple_inheritance # The_diamond_problem )

Probablemente olvide algunos puntos, pero espero que pueda aclarar las cosas.

PD: El "es un" / "debería hacer" viene de la respuesta de Vivek Vermani, ¡no quise robar su respuesta, solo para reutilizar los términos porque me gustaron!

IEatBagels
fuente
2
La palabra que estás buscando es comestible, creo.
Travis Weston
1
En realidad, creo que es un "Verbo", una "palabra que hace"
Grizly
7

Las diferencias técnicas entre una clase abstracta y una interfaz ya se enumeran en las otras respuestas con precisión. Quiero agregar una explicación para elegir entre una clase y una interfaz mientras escribo el código por el bien de la programación orientada a objetos.

Una clase debe representar una entidad, mientras que una interfaz debe representar el comportamiento.

Pongamos un ejemplo. Un monitor de computadora es una entidad y debe representarse como una clase.

class Monitor{
    private int monitorNo;
}

Está diseñado para proporcionarle una interfaz de pantalla, por lo que la funcionalidad debe estar definida por una interfaz.

interface Display{
    void display();
}

Hay muchas otras cosas a considerar como se explica en las otras respuestas, pero esta es la cosa más básica que la mayoría de la gente ignora al codificar.

Anurag Sharma
fuente
2
PHP no define los tipos de retorno y el OP etiquetó esta pregunta conPHP
Purefan
1

Solo quería agregar un ejemplo de cuándo puede necesitar usar ambos. Actualmente estoy escribiendo un controlador de archivos vinculado a un modelo de base de datos en una solución ERP de propósito general.

  • Tengo varias clases abstractas que manejan la falta estándar y también algunas funcionalidades especiales como conversión y transmisión para diferentes categorías de archivos.
  • La interfaz de acceso a archivos define un conjunto común de métodos necesarios para obtener, almacenar y eliminar un archivo.

De esta manera, tengo múltiples plantillas para diferentes archivos y un conjunto común de métodos de interfaz con una clara distinción. La interfaz proporciona la analogía correcta a los métodos de acceso en lugar de lo que hubiera sido con una clase abstracta base.

Más adelante, cuando haga adaptadores para diferentes servicios de almacenamiento de archivos, esta implementación permitirá que la interfaz se use en otros lugares en contextos totalmente diferentes.

Umair Ahmed
fuente