✅ WEB- und WordPress-Nachrichten, Themen, Plugins. Hier teilen wir Tipps und beste Website-Lösungen.

Abstrakte Klassen, Teil 1 – Abstraktes Verhalten

24

Vor ungefähr einem Monat schrieb ich über eine der Säulen der objektorientierten Programmierung (insbesondere Abstraktion). In dem Beitrag habe ich Abstraktion wie folgt definiert:

Stattdessen werden wir Ideen in ihre Klassen abstrahieren. Und hier gibt es eine Schlüsselidee: Eine Klasse sollte ein Substantiv darstellen.

Und obwohl das immer noch stimmt, ist die Idee abstrakter Klassen in der objektorientierten Programmierung etwas anderes.

Es klingt verwirrend, oder? Das ist:

  • Auf einer Ebene haben wir Abstraktion definiert als die Idee, dass wir eine Idee nehmen und sie in einer Klasse darstellen,
  • Auf einer anderen Ebene haben wir abstrakte Klassen, die dazu dienen, Funktionen zu definieren, die von Unterklassen implementiert werden müssen.

Und als ob das nicht verwirrend genug wäre, mischen wir das mit Schnittstellen, die einen Vertrag bereitstellen, dem implementierende Klassen folgen müssen, und dann mischen wir es mit abstrakten Klassen, die Methoden definieren, die ebenfalls implementiert werden müssen, aber auch eigene Methoden implementieren können.

Noch verwirrt? Keine Bange. Der ganze Sinn der nächsten drei Posts besteht darin, Folgendes zu tun:

  1. Definieren Sie, was abstrakte Klassen sind,
  2. Beschreiben Sie die unterschiedlichen abstrakten Klassen und Schnittstellen,
  3. Helfen Sie bei der Entscheidung, wann Sie eines über dem anderen verwenden möchten.

Nachdem das gesagt ist, hier ist die ganze Idee hinter abstrakten Klassen.

Abstrahierendes Verhalten

In erster Linie gibt es einen Unterschied zwischen Abstraktion und abstrakten Klassen. Ersteres bezieht sich auf die Idee, etwas in der Programmierung darzustellen; Letzteres bezieht sich auf eine tatsächliche Art, Code zu schreiben.

Und eine der besten Möglichkeiten, die ich gefunden habe, um über abstrakte Klassen in der objektorientierten Programmierung nachzudenken, ist, sie so zu betrachten:

Abstrakte Klassen sind ein Ersatz für die Implementierung.

Vielleicht kann man sie sich auch als Platzhalter vorstellen. Letztendlich stellen sie das Verhalten bereit, das die Unterklassen implementieren müssen.

Wie unterscheidet sich das von einer Schnittstelle? Denken Sie daran, dass eine Schnittstelle eine Signatur für eine Funktion (den Funktionsnamen, ihre Argumente und ihre Sichtbarkeitsmodifikatoren) definiert, die eine Klasse implementieren muss.

Abstraktion hingegen bietet einen Ersatz für die Implementierung, die eine Unterklasse implementieren muss. Aber vielleicht lässt sich dies am besten durch die Verwendung von Code demonstrieren.

Abstraktion in der Praxis

Angenommen, Sie arbeiten an einem Projekt und stellen fest, dass Sie Funktionen haben, die an mehr als einem Ort vorhanden sind. Abgesehen davon, dass es gegen die ganze Idee von DRY verstößt, hat es auch das Potenzial, ein Ort zu sein, an dem Sie Funktionalität in eine Basisklasse abstrahieren und erneut verwenden können.

Betrachten wir dies im Kontext eines Publishing-Systems. WordPress implementiert es nicht unbedingt so, aber es verwendet eine Idee, mit der wir vertraut sind: Taxonomien.

Denken Sie daran, dass wir in WordPress Tags und Kategorien haben. Es gibt subtile Unterschiede zwischen den beiden (z. B. ob man hierarchisch ist oder nicht), aber sie haben auch ähnliche Attribute wie einen Namen und einen Slug.

Eine Taxonomie-Abstraktion

Wir können also damit beginnen, eine abstrakte Taxonomieklasse zu schreiben, die die allgemeine Funktionalität in eine eigene Klasse abstrahiert.

<?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();

}

Im obigen Code sehen Sie, dass ich Folgendes getan habe:

  • deklariert die Klasse abstrakt
  • mehrere Attribute definiert, die im Konstruktor gesetzt werden
  • mehrere öffentliche Funktionen mit der Umsetzung versehen,
  • mehrere geschützte Methoden hinzugefügt.

Der Schlüssel zum Betrachten dieser Klasse ist, dass jede Klasse, die diese abstrakte Klasse implementiert, automatisch die Funktionalität hat, die im Konstruktor, der getName- Funktion und der getSlug- Funktion definiert ist.

Sie werden jedoch nicht die Implementierung der abstrakten Funktionen haben. Diese müssen noch von den Unterklassen implementiert werden (die ich gleich vorstellen werde).

Eine konkrete Taxonomie: Ein Tag

Nachdem wir nun eine abstrakte Klasse definiert haben, ist es möglich, die Abstraktion tatsächlich zu implementieren. Zum Beispiel :

<?php

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

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

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

Beachten Sie im obigen Code, dass die Klasse lediglich die Implementierung für die abstrakten Funktionen bereitstellt, die in der abstrakten Klasse definiert sind (die durch die Erweiterungsfunktion in der Klassendefinition angegeben wird).

Später in diesem Artikel werde ich erklären, wie dieser Code getestet wird, aber beachten Sie, dass der obige Code nicht nur die Funktionalität bietet, die Sie sehen, sondern auch die Funktionalität der Taxonomy -Klasse.

Eine konkrete Taxonomie: Eine Kategorie

Bevor ich mir das in Aktion anschaue, möchte ich auch eine Kategorie definieren. Dies umfasst Code, der Funktionen aus der abstrakten Klasse implementiert, aber auch eigene Funktionen.

Siehe unten:

<?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;
  }
}

Hier haben wir alles, was mit der Taxonomy -Klasse geliefert wird, aber wir haben auch unsere Eigenschaft für ihre übergeordnete ID und Getter- und Setter-Methoden definiert. Obwohl es in diesem Fall trivial ist, zeigt es, wie Kategorien, die hierarchisch sind, funktionieren können.

Wenn die Kategorie kein übergeordnetes Element hat, wird die ID außerdem auf -1 gesetzt, was das Schreiben für automatisierte Tests erleichtert oder sogar überprüft, ob sie ein übergeordnetes Element hat.

Es in Aktion sehen

Um diesen gesamten Code zu demonstrieren, habe ich eine Zusammenfassung, die den gesamten Code in einer einzigen Datei enthält. Als bewährte Methode empfehle ich dies nicht. Stattdessen sollte jede Klasse in einer eigenen Datei gehalten werden, und jede Klasse sollte zu einem Namensraum gehören.

Da dies aber nur zu Demonstrationszwecken dient, reicht es aus.

<?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();

Wenn Sie dies in der Konsole ausführen, sollten Sie etwa die folgende Ausgabe sehen:

Möglicherweise müssen Sie einige echo -Anweisungen hinzufügen, um sicherzustellen, dass neue Zeilen erstellt werden, aber das liegt an Ihnen.

Was ist mit Schnittstellen?

Also an dieser Stelle:

  • wir haben eine Arbeitsdefinition dessen, was abstrakte Klassen sind,
  • wir haben ein Beispiel dafür, wie abstrakte Klassen aussehen,
  • und wir haben eine funktionierende Demo, wie sie funktionieren können.

Als Nächstes werde ich tiefer in die Diskussion der Unterschiede zwischen abstrakten Klassen und Schnittstellen eintauchen, wenn Sie vielleicht eine über der anderen verwenden möchten oder wann Sie sie in Verbindung miteinander verwenden möchten.

Aufnahmequelle: tommcfarlin.com

Diese Website verwendet Cookies, um Ihre Erfahrung zu verbessern. Wir gehen davon aus, dass Sie damit einverstanden sind, Sie können sich jedoch abmelden, wenn Sie möchten. Annehmen Weiterlesen