✅ Notizie, temi, plugin WEB e WordPress. Qui condividiamo suggerimenti e le migliori soluzioni per siti web.

Classi astratte, parte 1 – Comportamento astratto

16

Circa un mese fa, ho scritto di uno dei pilastri della programmazione orientata agli oggetti (in particolare l’astrazione). Nel post, ho definito l’astrazione come segue:

Invece, astrarremo le idee nelle loro classi. E qui c’è un’idea chiave: una classe dovrebbe rappresentare un sostantivo.

E sebbene ciò sia ancora vero, l’idea di classi astratte è qualcosa di diverso nella programmazione orientata agli oggetti.

Sembra confuso, giusto? Questo è:

  • a un livello, abbiamo l’astrazione definita come l’idea che prendiamo un’idea e la rappresentiamo in una classe,
  • su un altro livello, abbiamo classi astratte che vengono utilizzate per aiutare a definire le funzioni che le sottoclassi devono implementare.

E se ciò non è abbastanza confuso, lo mescoliamo con interfacce che forniscono un contratto che le classi di implementazione devono seguire, e quindi lo mescoliamo con classi astratte che definiscono metodi che devono anche essere implementati ma possono anche implementare metodi propri.

Confuso ancora? Nessun problema. Il punto centrale dei prossimi tre post è di fare quanto segue:

  1. Definisci cosa sono le classi astratte,
  2. Descrivi le diverse classi e interfacce astratte,
  3. Aiuta a decidere quando vuoi usare uno sull’altro.

Detto questo, ecco l’intera idea dietro le classi astratte.

Comportamento astratto

Innanzitutto, c’è una differenza nell’astrazione e nelle classi astratte. Il primo si riferisce all’idea di rappresentare qualcosa nella programmazione; quest’ultimo si riferisce a un modo effettivo di scrivere codice.

E uno dei modi migliori che ho trovato per pensare alle classi astratte nella programmazione orientata agli oggetti è pensarle in questo modo:

Le classi astratte sostituiscono l’implementazione.

Forse un altro modo di pensarli sono i segnaposto. Infine, forniscono il comportamento che le sottoclassi devono implementare.

In che modo è diverso da un’interfaccia? Ricorda che un’interfaccia definisce una firma per una funzione (il nome della funzione, i suoi argomenti e i suoi modificatori di visibilità) che una classe deve implementare.

L’astrazione, d’altra parte, fornisce un sostituto per l’implementazione che una sottoclasse deve implementare. Ma forse questo è meglio dimostrato attraverso l’uso del codice.

L’astrazione in pratica

Diciamo che stai lavorando a un progetto e scopri di avere funzionalità che esistono in più di un posto. Oltre a violare l’intera idea di DRY, ha anche il potenziale per essere un luogo in cui è possibile astrarre funzionalità in una classe base e riutilizzarla.

Consideriamo questo nel contesto di un sistema editoriale. Questo non è necessariamente il modo in cui WordPress lo implementa, ma utilizza un’idea che ci è familiare: le tassonomie.

In WordPress, ricorda che abbiamo Tag e Categorie. Ci sono sottili differenze tra i due (come se uno fosse gerarchico o meno), ma condividono anche attributi simili come avere un nome e una lumaca.

Un’astrazione tassonomica

Quindi possiamo iniziare scrivendo una classe Tassonomia astratta che astrae la funzionalità comune nella propria classe.

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

}

Nel codice sopra, vedrai che ho fatto quanto segue:

  • ha dichiarato l’ abstract della classe
  • definito diversi attributi che verranno impostati nel costruttore
  • fornito diverse funzioni pubbliche con l’attuazione,
  • aggiunto diversi metodi protetti.

Il punto chiave dall’esame di questa classe è che qualsiasi classe che implementa questa classe astratta avrà automaticamente la funzionalità definita nel costruttore, la funzione getName e la funzione getSlug.

Non avranno, tuttavia, l’implementazione delle funzioni astratte. Questi sono ciò che resta da implementare dalle sottoclassi (che condividerò momentaneamente).

Una tassonomia concreta: un tag

Ora che abbiamo una classe astratta definita, è possibile implementare effettivamente l’astrazione. Ad esempio :

<?php

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

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

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

Nel codice sopra, si noti che tutto ciò che la classe fa è fornire l’implementazione per le funzioni astratte definite nella classe astratta (che è specificata dalla funzione extends nella definizione della classe).

Più avanti in questo articolo, condividerò come testare questo codice, ma noterò che quanto sopra offre non solo la funzionalità che vedi, ma anche la funzionalità della classe Tassonomia.

Una tassonomia concreta: una categoria

Prima di dare un’occhiata a questo in azione, voglio definire anche una categoria. Ciò includerà codice che implementa funzioni dalla classe astratta ma anche funzioni proprie.

Vedi sotto:

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

Qui abbiamo tutto ciò che viene fornito con la classe Tassonomia, ma abbiamo anche definito la nostra proprietà per il suo ID genitore e metodi getter e setter. Anche se banale in questo caso, mostra come le categorie, che sono gerarchiche, possono funzionare.

Inoltre, se la categoria non ha un genitore, l’ID è impostato su -1, il che semplifica la scrittura per i test automatici o persino il controllo per vedere se ha un genitore.

Vedendolo in azione

Per dimostrare l’intero codice, ho un’idea che include tutto il codice in un unico file. Come best practice, non lo consiglio. Invece, ogni classe dovrebbe essere conservata nel proprio file e ogni classe dovrebbe appartenere a uno spazio dei nomi.

Ma poiché questo è puramente a scopo dimostrativo, è sufficiente.

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

Quando lo esegui nella console, dovresti vedere qualcosa come il seguente output:

Potrebbe essere necessario aggiungere alcune dichiarazioni di eco per assicurarti che stia creando nuove linee, ma dipende da te.

E le interfacce?

Quindi, a questo punto:

  • abbiamo una definizione funzionante di cosa sono le classi astratte,
  • abbiamo un esempio di come appaiono le classi astratte,
  • e abbiamo una demo funzionante di come possono esibirsi.

Successivamente, farò un tuffo più approfondito nella discussione delle differenze tra classi e interfacce astratte, quando potresti voler usare l’una sull’altra o quando potresti volerle usare insieme l’una all’altra.

Fonte di registrazione: 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