✅ Noticias, temas, complementos de WEB y WordPress. Aquí compartimos consejos y las mejores soluciones para sitios web.

Clases abstractas, Parte 1 – Comportamiento de abstracción

30

Hace aproximadamente un mes, escribí sobre uno de los pilares de la programación orientada a objetos (específicamente Abstracción). En la publicación, definí la abstracción de la siguiente manera:

En cambio, vamos a abstraer ideas en sus clases. Y aquí hay una idea clave: una clase debe representar un sustantivo.

Y aunque eso sigue siendo cierto, la idea de las clases abstractas es algo diferente en la programación orientada a objetos.

Suena confuso, ¿verdad? Eso es:

  • en un nivel, tenemos la abstracción definida como la idea de que tomamos una idea y la representamos en una clase,
  • en otro nivel, tenemos clases abstractas que se utilizan para ayudar a definir funciones que deben implementar las subclases.

Y si eso no es lo suficientemente confuso, mezclamos esto con interfaces que proporcionan un contrato que deben seguir las clases de implementación, y luego lo mezclamos con clases abstractas que definen métodos que también deben implementarse pero que también pueden implementar métodos propios.

¿Confundido todavía? Sin preocupaciones. El objetivo de las próximas tres publicaciones es hacer lo siguiente:

  1. Definir qué son las clases abstractas,
  2. Describir las diferentes clases e interfaces abstractas,
  3. Ayuda a decidir cuándo quieres usar uno sobre el otro.

Dicho esto, aquí está toda la idea detrás de las clases abstractas.

Comportamiento de abstracción

En primer lugar, hay una diferencia en la abstracción y las clases abstractas. El primero se refiere a la idea de representar algo en la programación; el último se refiere a una forma real de escribir código.

Y una de las mejores formas que he encontrado para pensar en las clases abstractas en la programación orientada a objetos es pensar en ellas de esta manera:

Las clases abstractas son un sustituto de la implementación.

Quizás otra forma de pensar en ellos es como marcadores de posición. En última instancia, proporcionan el comportamiento que deben implementar las subclases.

¿En qué se diferencia esto de una interfaz? Recuerde que una interfaz define una firma para una función (el nombre de la función, sus argumentos y sus modificadores de visibilidad) que debe implementar una clase.

La abstracción, por otro lado, proporciona un sustituto para la implementación que debe implementar una subclase. Pero quizás esto se demuestre mejor mediante el uso de código.

Abstracción en la práctica

Digamos que está trabajando en un proyecto y descubre que tiene una funcionalidad que existe en más de un lugar. Además de violar toda la idea de DRY, también tiene el potencial de ser un lugar donde puede abstraer la funcionalidad en una clase base y usarla nuevamente.

Consideremos esto en el contexto de un sistema de publicación. No es necesariamente así como WordPress lo implementa, pero usa una idea con la que estamos familiarizados: las taxonomías.

En WordPress, recuerda que tenemos Etiquetas y Categorías. Hay diferencias sutiles entre los dos (como si uno es jerárquico o no), pero también comparten atributos similares, como tener un nombre y un slug.

Una abstracción taxonómica

Entonces, podemos comenzar escribiendo una clase de taxonomía abstracta que abstraiga la funcionalidad común en su propia clase.

<?php

abstract class Taxonomy
{

  private $taxonomyName;

  private $taxonomySlug;

  public function __construct($name) {
    $this->taxonomyName = $name;
    $this->taxonomySlug = strtolower(str_ireplace(' ', '-', $this->taxonomyName));
  }

  public function getName() {
    return $this->taxonomyName;
  }

  public function getSlug() {
    return $this->taxonomySlug;
  }

  abstract protected function isHierarchical();
  abstract protected function isCategory();
  abstract protected function isTag();

}

En el código anterior, verá que he hecho lo siguiente:

  • declaró la clase abstracta
  • definió varios atributos que se establecerán en el constructor
  • proporcionó varias funciones públicas con la implementación,
  • agregó varios métodos protegidos.

La conclusión clave al observar esta clase es que cualquier clase que implemente esta clase abstracta tendrá automáticamente la funcionalidad definida en el constructor, la función getName y la función getSlug.

Sin embargo, no tendrán la implementación de las funciones abstractas. Eso es lo que queda para ser implementado por las subclases (que compartiré momentáneamente).

Una taxonomía concreta: una etiqueta

Ahora que tenemos una clase abstracta definida, es posible implementar la abstracción. Por ejemplo :

<?php

class Tag extends Taxonomy
{
  protected function isHierarchical() {
    return false;
  }

  protected function isCategory() {
    return $this->isHierarchical;
  }

  protected function isTag() {
    return !$this->isHierarchical;
  }
}

En el código anterior, observe que todo lo que hace la clase es proporcionar la implementación de las funciones abstractas definidas en la clase abstracta (que se especifica mediante la función extends en la definición de clase).

Más adelante en este artículo, compartiré cómo probar este código, pero tenga en cuenta que lo anterior no solo ofrece la funcionalidad que ve, sino también la funcionalidad de la clase Taxonomy.

Una taxonomía concreta: una categoría

Antes de echar un vistazo a esto en acción, también quiero definir una categoría. Esto incluirá código que implemente funciones de la clase abstracta pero también funciones propias.

Vea abajo:

<?php
class Category extends Taxonomy
{
  private $parentId = -1;

  protected function isHierarchical() {
    return true;
  }

  protected function isCategory() {
    return $this->isHierarchical;
  }

  protected function isTag() {
    return !$this->isHierarchical;
  }

  public function setParentId($parentId) {
    $this->parentId = $parentId;
  }

  public function getParentId() {
    return $this->parentId;
  }
}

Aquí tenemos todo lo que viene con la clase Taxonomy, pero también hemos definido nuestra propiedad para su ID principal y métodos getter y setter. Aunque trivial en este caso, muestra cómo pueden funcionar las categorías, que son jerárquicas.

Además, si la categoría no tiene un padre, entonces la ID se establece en -1, lo que facilita la escritura para pruebas automatizadas o incluso verificar si tiene un padre.

Verlo en acción

Para hacer una demostración de todo este código, tengo una idea general que incluye todo el código en un solo archivo. Como mejor práctica, no recomiendo esto. En su lugar, cada clase debe mantenerse en su propio archivo y cada clase debe pertenecer a un espacio de nombres.

Pero dado que esto es puramente para fines de demostración, es suficiente.

<?php

abstract class Taxonomy
{
  private $taxonomyName;

  private $taxonomySlug;

  public function __construct($name) {
    $this->taxonomyName = $name;
    $this->taxonomySlug = strtolower(str_ireplace(' ', '-', $this->taxonomyName));
  }

  public function getName() {
    return $this->taxonomyName;
  }

  public function getSlug() {
    return $this->taxonomySlug;
  }

  abstract protected function isHierarchical();
  abstract protected function isCategory();
  abstract protected function isTag();

}

/*--*/

class Tag extends Taxonomy
{
  protected function isHierarchical() {
    return false;
  }

  protected function isCategory() {
    return $this->isHierarchical;
  }

  protected function isTag() {
    return !$this->isHierarchical;
  }
}

/*--*/

class Category extends Taxonomy
{
  private $parentId = -1;

  protected function isHierarchical() {
    return true;
  }

  protected function isCategory() {
    return $this->isHierarchical;
  }

  protected function isTag() {
    return !$this->isHierarchical;
  }

  public function setParentId($parentId) {
    $this->parentId = $parentId;
  }

  public function getParentId() {
    return $this->parentId;
  }
}

/*- Tag Demo ----------------------------*/

$tag = new Tag('Acme Tag');
echo $tag->getName();
echo $tag->getSlug();

/*- Category Demo -----------------------*/

$category = new Category('Acme Category');

echo $category->getName();
echo $category->getSlug();
echo $category->getParentId();

$category->setParentId(100);
echo $category->getparentId();

Cuando ejecute esto en la consola, debería ver algo como el siguiente resultado:

Es posible que deba agregar algunas declaraciones de eco para asegurarse de que está creando nuevas líneas, pero eso depende de usted.

¿Qué pasa con las interfaces?

Entonces, en este punto:

  • tenemos una definición funcional de lo que son las clases abstractas,
  • tenemos un ejemplo de cómo se ven las clases abstractas,
  • y tenemos una demostración funcional de cómo pueden funcionar.

A continuación, me sumergiré más profundamente en la discusión de las diferencias entre las clases abstractas y las interfaces, cuándo puede querer usar una sobre la otra o cuándo puede querer usarlas en conjunto.

Fuente de grabación: tommcfarlin.com

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More