✅ Новости WEB и WordPress, темы, плагины. Здесь мы делимся советами и лучшими решениями для веб-сайтов.

Абстрактные классы, часть 1 — абстрагирующее поведение

29

Около месяца назад я писал об одном из столпов объектно-ориентированного программирования (в частности, об абстракции). В посте я определил абстракцию следующим образом:

Вместо этого мы собираемся абстрагировать идеи в их классы. И здесь есть ключевая идея: класс должен представлять существительное.

И хотя это все еще верно, идея абстрактных классов отличается от объектно-ориентированного программирования.

Звучит запутанно, правда? То есть:

  • на одном уровне у нас есть абстракция, определяемая как идея, что мы берем идею и представляем ее в классе,
  • на другом уровне у нас есть абстрактные классы, которые помогают определять функции, которые должны реализовывать подклассы.

И если это не достаточно запутанно, мы смешиваем это с интерфейсами, которые обеспечивают выполнение классов, которым должны следовать контракты, а затем мы смешиваем это с абстрактными классами, которые определяют методы, которые также должны быть реализованы, но также могут реализовывать собственные методы.

Еще не запутались? Без проблем. Весь смысл следующих трех постов в том, чтобы сделать следующее:

  1. Определите, что такое абстрактные классы,
  2. Описывать различные абстрактные классы и интерфейсы,
  3. Помогите решить, когда вы хотите использовать один над другим.

С учетом сказанного, вот вся идея абстрактных классов.

Абстрагирующее поведение

Прежде всего, есть разница в абстракции и абстрактных классах. Первый относится к идее представления чего-либо в программировании; последний относится к реальному способу написания кода.

И один из лучших способов, которые я нашел для того, чтобы думать об абстрактных классах в объектно-ориентированном программировании, состоит в том, чтобы думать о них так:

Абстрактные классы заменяют реализацию.

Возможно, их можно рассматривать как заполнители. В конечном счете, они обеспечивают поведение, которое должны реализовать подклассы.

Чем это отличается от интерфейса? Помните, что интерфейс определяет сигнатуру для функции (имя функции, ее аргументы и модификаторы видимости), которую должен реализовать класс.

Абстракция, с другой стороны, обеспечивает замену реализации, которую должен реализовать подкласс. Но, возможно, это лучше всего демонстрируется с помощью кода.

Абстракция на практике

Допустим, вы работаете над проектом и обнаружили, что у вас есть функциональность, существующая более чем в одном месте. Помимо нарушения всей идеи DRY, он также может стать местом, где вы можете абстрагировать функциональность в базовый класс и использовать его снова.

Давайте рассмотрим это в контексте издательской системы. Это не обязательно то, как это реализует WordPress, но он использует идею, с которой мы знакомы: таксономии.

Вспомним, что в WordPress у нас есть Теги и Категории. Между ними есть тонкие различия (например, иерархический он или нет), но они также имеют схожие атрибуты, такие как имя и ярлык.

Абстракция таксономии

Итак, мы можем начать с написания абстрактного класса таксономии, который абстрагирует общую функциональность в свой собственный класс.

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

}

В приведенном выше коде вы увидите, что я сделал следующее:

  • объявил абстрактный класс
  • определены несколько атрибутов, которые будут установлены в конструкторе
  • обеспечил реализацию нескольких государственных функций,
  • добавлено несколько защищенных методов.

Ключевым моментом при рассмотрении этого класса является то, что любой класс, реализующий этот абстрактный класс, автоматически будет иметь функциональные возможности, определенные в конструкторе, функции getName и функции getSlug.

Однако у них не будет реализации абстрактных функций. Это то, что осталось реализовать с помощью подклассов (которыми я сейчас поделюсь).

Конкретная таксономия: тег

Теперь, когда мы определили абстрактный класс, можно реализовать абстракцию. Например :

<?php

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

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

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

Обратите внимание, что в приведенном выше коде все, что делает класс, — это обеспечивает реализацию абстрактных функций, определенных в абстрактном классе (который указан функцией extends в определении класса).

Позже в этой статье я расскажу, как протестировать этот код, но обратите внимание, что вышеприведенное предлагает не только функциональность, которую вы видите, но и функциональность класса Taxonomy.

Конкретная таксономия: категория

Прежде чем взглянуть на это в действии, я также хочу определить категорию. Это будет включать код, который реализует функции из абстрактного класса, а также собственные функции.

Смотри ниже:

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

Здесь у нас есть все, что поставляется с классом Taxonomy, но мы также определили наше свойство для его родительского идентификатора и методов получения и установки. Хотя в данном случае это тривиально, он показывает, как могут функционировать иерархические категории.

Кроме того, если у категории нет родителя, то для идентификатора устанавливается значение -1, что упрощает запись для автоматического тестирования или даже проверки наличия родителя.

Видеть это в действии

Чтобы продемонстрировать весь этот код, у меня есть суть, которая включает весь код в один файл. Как лучшая практика, я не рекомендую это. Вместо этого каждый класс должен храниться в своем собственном файле, и каждый класс должен принадлежать пространству имен.

Но поскольку это чисто для демонстрационных целей, этого достаточно.

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

Когда вы запустите это в консоли, вы должны увидеть что-то вроде следующего вывода:

Возможно, вам придется добавить несколько операторов эха, чтобы убедиться, что он создает новые строки, но это зависит от вас.

Что насчет интерфейсов?

Итак, на данный момент:

  • у нас есть рабочее определение того, что такое абстрактные классы,
  • у нас есть пример того, как выглядят абстрактные классы,
  • и у нас есть рабочая демонстрация того, как они могут работать.

Далее я углублюсь в обсуждение различий между абстрактными классами и интерфейсами, когда вы можете захотеть использовать один вместо другого или когда вы можете захотеть использовать их в сочетании друг с другом.

Источник записи: tommcfarlin.com

Этот веб-сайт использует файлы cookie для улучшения вашего опыта. Мы предполагаем, что вы согласны с этим, но вы можете отказаться, если хотите. Принимаю Подробнее