✅ Nowości, motywy, wtyczki WEB i WordPress. Tutaj dzielimy się wskazówkami i najlepszymi rozwiązaniami dla stron internetowych.

Klasy abstrakcyjne, część 1 — zachowanie abstrakcji

17

Około miesiąc temu pisałem o jednym z filarów programowania obiektowego (a konkretnie o abstrakcji). W poście zdefiniowałem abstrakcję jako:

Zamiast tego będziemy abstrahować pomysły do ​​ich klas. I tutaj jest kluczowa idea: klasa powinna reprezentować rzeczownik.

I choć to wciąż prawda, idea klas abstrakcyjnych jest czymś innym w programowaniu obiektowym.

Brzmi dezorientująco, prawda? To znaczy:

  • na jednym poziomie mamy abstrakcję definiowaną jako pomysł, że bierzemy ideę i reprezentujemy ją w klasie,
  • na innym poziomie mamy klasy abstrakcyjne, które pomagają zdefiniować funkcje, które muszą zaimplementować podklasy.

A jeśli to nie jest wystarczająco zagmatwane, mieszamy to z interfejsami, które dostarczają kontraktu, które muszą następować, a następnie mieszamy z klasami abstrakcyjnymi, które definiują metody, które również muszą być zaimplementowane, ale mogą również implementować własne metody.

Zdezorientowany? Bez obaw. Celem kolejnych trzech postów jest wykonanie następujących czynności:

  1. Zdefiniuj czym są klasy abstrakcyjne,
  2. Opisz różnice w klasach abstrakcyjnych i interfejsach,
  3. Pomóż zdecydować, kiedy chcesz użyć jednego z drugim.

Powiedziawszy to, oto cała idea klas abstrakcyjnych.

Abstrakcyjne zachowanie

Przede wszystkim istnieje różnica w klasach abstrakcyjnych i abstrakcyjnych. Pierwsza odnosi się do idei reprezentowania czegoś w programowaniu; to ostatnie odnosi się do rzeczywistego sposobu pisania kodu.

A jednym z najlepszych sposobów, jakie znalazłem na myślenie o klasach abstrakcyjnych w programowaniu obiektowym, jest myślenie o nich w następujący sposób:

Klasy abstrakcyjne są substytutem implementacji.

Być może innym sposobem myślenia o nich są symbole zastępcze. Ostatecznie zapewniają zachowanie, które podklasy muszą zaimplementować.

Czym to się różni od interfejsu? Pamiętaj, że interfejs definiuje sygnaturę funkcji (nazwę funkcji, jej argumenty i jej modyfikatory widoczności), którą klasa musi zaimplementować.

Z drugiej strony abstrakcja zapewnia substytut implementacji, którą musi zaimplementować podklasa. Ale być może najlepiej jest to zademonstrować za pomocą kodu.

Abstrakcja w praktyce

Załóżmy, że pracujesz nad projektem i odkrywasz, że masz funkcjonalność, która istnieje w więcej niż jednym miejscu. Oprócz naruszania całej idei DRY, ma również potencjał, aby być miejscem, w którym można wyabstrahować funkcjonalność do klasy bazowej i użyć jej ponownie.

Rozważmy to w kontekście systemu wydawniczego. Niekoniecznie w taki sposób implementuje go WordPress, ale wykorzystuje ideę, którą znamy: taksonomie.

W WordPressie przypomnij sobie, że mamy Tagi i kategorie. Istnieją subtelne różnice między tymi dwoma (na przykład, czy jeden, jeśli jest hierarchiczny, czy nie), ale mają również podobne atrybuty, takie jak posiadanie imienia i ślimaka.

Abstrakcja taksonomiczna

Możemy więc zacząć od napisania abstrakcyjnej klasy Taxonomy, która abstrahuje wspólną funkcjonalność do własnej klasy.

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

}

W powyższym kodzie zobaczysz, że wykonałem następujące czynności:

  • zadeklarował abstrakt klasy
  • zdefiniował kilka atrybutów, które zostaną ustawione w konstruktorze
  • realizował kilka funkcji publicznych,
  • dodano kilka chronionych metod.

Kluczem od spojrzenia na tę klasę jest to, że każda klasa, która implementuje tę klasę abstrakcyjną, automatycznie będzie miała funkcjonalność zdefiniowaną w konstruktorze, funkcji getName i funkcji getSlug.

Nie będą jednak miały implementacji funkcji abstrakcyjnych. To jest to, co pozostało do zaimplementowania przez podklasy (które za chwilę udostępnię).

Konkretna taksonomia: tag

Teraz, gdy mamy zdefiniowaną klasę abstrakcyjną, można faktycznie zaimplementować abstrakcję. Na przykład :

<?php

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

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

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

Zauważ, że w powyższym kodzie klasa zapewnia implementację funkcji abstrakcyjnych zdefiniowanych w klasie abstrakcyjnej (co jest określone przez funkcję extends w definicji klasy).

W dalszej części tego artykułu podzielę się, jak przetestować ten kod, ale zauważ, że powyższe nie tylko oferuje funkcjonalność, którą widzisz, ale także funkcjonalność klasy Taxonomy .

Konkretna taksonomia: kategoria

Zanim przyjrzę się temu w akcji, chcę również zdefiniować kategorię. Obejmuje to kod, który implementuje funkcje z klasy abstrakcyjnej, ale także funkcje własne.

Zobacz poniżej:

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

Tutaj mamy wszystko, co pochodzi z klasy Taxonomy, ale zdefiniowaliśmy również naszą właściwość dla jej identyfikatora rodzica oraz metod pobierających i ustawiających. Choć w tym przypadku banalna, pokazuje, jak mogą funkcjonować kategorie, które są hierarchiczne.

Ponadto, jeśli kategoria nie ma rodzica, identyfikator jest ustawiony na -1, co ułatwia pisanie do automatycznego testowania, a nawet sprawdzanie, czy ma rodzica.

Widząc to w akcji

Aby zademonstrować cały kod, mam sedno, które zawiera cały kod w jednym pliku. Jako najlepsza praktyka nie polecam tego. Zamiast tego każda klasa powinna być przechowywana w osobnym pliku, a każda klasa powinna należeć do przestrzeni nazw.

Ale ponieważ służy to wyłącznie celom demonstracyjnym, wystarczy.

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

Po uruchomieniu tego w konsoli powinieneś zobaczyć coś takiego jak następujące dane wyjściowe:

Być może będziesz musiał dodać kilka stwierdzeń echa, aby upewnić się, że tworzy nowe linie, ale to zależy od Ciebie.

A co z interfejsami?

W tym momencie:

  • mamy roboczą definicję tego, czym są klasy abstrakcyjne,
  • mamy przykład jak wyglądają klasy abstrakcyjne,
  • i mamy działające demo tego, jak mogą działać.

Następnie zagłębię się w omówienie różnic między klasami abstrakcyjnymi a interfejsami, kiedy możesz chcieć użyć jednego z drugim lub kiedy możesz chcieć użyć ich w połączeniu ze sobą.

Źródło nagrywania: tommcfarlin.com

Ta strona korzysta z plików cookie, aby poprawić Twoje wrażenia. Zakładamy, że nie masz nic przeciwko, ale możesz zrezygnować, jeśli chcesz. Akceptuję Więcej szczegółów