Maison > développement back-end > tutoriel php > Polymorphisme du sous-type - Échange de mise en œuvre au moment de l'exécution

Polymorphisme du sous-type - Échange de mise en œuvre au moment de l'exécution

尊渡假赌尊渡假赌尊渡假赌
Libérer: 2025-02-25 18:15:16
original
607 Les gens l'ont consulté

Subtype Polymorphism - Swapping Implementation at Runtime

Points de base

    Le polymorphisme
  • sous-type dans la conception orientée objet fait référence à la capacité d'un système à définir un ensemble de contrats ou d'interfaces, puis de les mettre en œuvre par différents sous-types. Ceci est crucial pour la conception de systèmes évolutifs qui peuvent consommer des contrats spécifiques sans vérifier si l'implémentateur est dans le type attendu.
  • Cet article démontre l'utilisation de polymorphismes de sous-type en développant un composant de cache insérable qui peut être étendu pour répondre aux besoins des utilisateurs en développant des pilotes de cache supplémentaires.
  • Une caractéristique clé du composant de cache est sa capacité à échanger différents pilotes de cache à l'exécution sans modifier aucun code client. Ceci est réalisé en définissant un contrat de cache qui est ensuite suivi de différentes implémentations, profitant ainsi du polymorphisme.
  • Le composant de cache peut changer de backends au moment de l'exécution, mettant en évidence l'importance du polymorphisme dans la conception de modules hautement découplés. Cela permet une reconnexion facile au moment de l'exécution sans provoquer une vulnérabilité ou des problèmes rigides dans d'autres parties du système.
  • Le polymorphisme de sous-type
  • rend non seulement le système plus orthogonal et plus facile à évoluer, mais il est également moins susceptible de violer les paradigmes de base tels que le principe ouvert / fermé et le principe de la programmation "axée sur l'interface". Il s'agit d'un aspect fondamental de la programmation orientée objet qui permet la flexibilité et la réutilisabilité du code.

Beaucoup de gens peuvent douter de la corrélation entre l'héritage et le polymorphisme dans la conception orientée objet? Probablement peu, la plupart d'entre eux peuvent être dus à l'ignorance ou à la pensée étroite. Mais il y a un petit problème ici qui ne peut pas être ignoré. Bien qu'il soit simple de comprendre la logique de l'héritage, les choses deviennent plus difficiles en se plongeant dans les détails du polymorphisme. Le terme «polymorphisme» est intimidant en soi, avec sa définition académique pleine de perspectives différentes, ce qui rend encore plus difficile de comprendre ce qui est réellement derrière. Les concepts périphériques tels que le polymorphisme des paramètres et le polymorphisme ad hoc (généralement implémenté par la suppression / surcharge de méthode) ont des applications importantes dans certains langages de programmation, mais dans la conception, ils peuvent consommer des contrats spécifiques (lectures) lorsqu'un système extensible abstrait, le dernier cas du cas devrait être abandonné sans vérifier si l'implémentateur est du type attendu. En bref, la plupart du temps, toute référence générale au polymorphisme dans la programmation orientée objet est implicitement considérée comme une capacité explicite du système utilisée pour définir un ensemble de contrats ou d'interfaces qui ou l'interface sont suivis de différentes implémentations. Ce polymorphisme "canonique" est souvent appelé polymorphisme de sous-type, car l'implémentateur d'une interface est considéré comme un sous-type d'entre eux, qu'il existe une véritable hiérarchie. Comme on pourrait s'y attendre, comprendre la nature du polymorphisme n'est que la moitié du processus d'apprentissage; "Code" (dans de nombreux cas, c'est un euphémisme bon marché pour le code de jouet). Dans cet article, je vais vous montrer comment profiter des avantages fournis par le polymorphisme en développant un composant de cache insérable. La fonctionnalité de base peut plus tard être étendue pour répondre à vos besoins en développant des pilotes de cache supplémentaires.

Définissez l'interface et la mise en œuvre des composants

Le menu d'options à choisir n'est en aucun cas absent lors de la construction de composants de cache extensibles (si vous êtes sceptique à ce sujet, regardez simplement ce qui se trouve derrière certains cadres populaires). Cependant, ici, les composants que je fournis ont la capacité intelligente d'échanger différents pilotes de cache à l'exécution sans modifier aucun code client. Alors, comment pouvez-vous faire cela sans beaucoup d'efforts pendant le processus de développement? Eh bien, la première étape devrait être ... Oui, définissez un contrat de cache isolé qui sera suivi de différentes implémentations plus tard, profitant ainsi des avantages du polymorphisme. À son niveau le plus élémentaire, le contrat ci-dessus est le suivant:

<?php namespace LibraryCache;

interface CacheInterface
{
    public function set($id, $data);
    public function get($id);
    public function delete($id);
    public function exists($id);
}
Copier après la connexion
Copier après la connexion
L'interface

CacheInterface est un contrat squelette qui résume le comportement des éléments de cache communs. Avec l'interface, vous pouvez facilement créer des implémentations de cache spécifiques conformes à leurs contrats. Puisque je veux rester simple et facile à comprendre, le pilote de cache que je configure sera juste un duo Lean: le premier utilise le système de fichiers comme backend sous-jacent pour les données de cache / get, tandis que la seconde utilise l'extension APC dans les coulisses. Ce qui suit est une implémentation de cache basée sur des fichiers:

<?php namespace LibraryCache;

class FileCache implements CacheInterface
{
    const DEFAULT_CACHE_DIRECTORY = 'Cache/';
    private $cacheDir;

    public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) {
        $this->setCacheDir($cacheDir);
    }

    public function setCacheDir($cacheDir) {
        if (!is_dir($cacheDir)) {
            if (!mkdir($cacheDir, 0644)) {
                throw InvalidArgumentException('The cache directory is invalid.');
            }
        }
        $this->cacheDir = $cacheDir;
        return $this;
    }

    public function set($id, $data) {
        if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) {
            throw new RuntimeException("Unable to cache the data with ID '$id'.");
        }
        return $this;
    }

    public function get($id) {
        if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) {
            throw new RuntimeException("Unable to get the data with ID '$id'.");
        }
        return $data;
    }

    public function delete($id) {
        if (!@unlink($this->cacheDir . $id)) {
            throw new RuntimeException("Unable to delete the data with ID '$id'.");
        }
        return $this;
    }

    public function exists($id) {
        return file_exists($this->cacheDir . $id);
    }
}
Copier après la connexion
Copier après la connexion
La logique de conduite de la classe devrait être facile à comprendre. Jusqu'à présent, la chose la plus pertinente est qu'elle expose un comportement polymorphe soigné, car il réalise fidèlement le premier

. Bien que cette capacité soit douce et charmante, pour sa part, je ne l'apprécierais pas compte tenu de l'objectif ici est de créer un composant de cache qui peut changer de backends au moment de l'exécution. Passons à des efforts supplémentaires à des fins d'enseignement et donnons vie à une autre implémentation rationalisée de FileCache. L'implémentation suivante est conforme au contrat d'interface, mais cette fois, c'est en utilisant APC pour étendre la méthode de regroupement: CacheInterface CacheInterface

<?php namespace LibraryCache;

class ApcCache implements CacheInterface
{
    public function set($id, $data, $lifeTime = 0) {
        if (!apc_store($id, $data, (int) $lifeTime)) {
            throw new RuntimeException("Unable to cache the data with ID '$id'.");
        }
    }

    public function get($id) {
        if (!$data = apc_fetch($id)) {
            throw new RuntimeException("Unable to get the data with ID '$id'.");
        } 
        return $data;
    }

    public function delete($id) {
        if (!apc_delete($id)) {
            throw new RuntimeException("Unable to delete the data with ID '$id'.");
        }
    }

    public function exists($id) {
        return apc_exists($id);
    }
}
Copier après la connexion
Copier après la connexion
La classe n'est pas l'emballage APC le plus éblouissant que vous ayez jamais vu dans votre carrière, il contient toutes les fonctionnalités dont vous avez besoin pour enregistrer, récupérer et supprimer des données de mémoire. Applaudissons-nous, car nous avons mis en œuvre avec succès un module de cache léger dont le backend spécifique est non seulement facile à échanger au moment de l'exécution en raison de son polymorphisme, mais il est également extrêmement facile d'ajouter plus de backends à l'avenir. Écrivez simplement une autre implémentation conforme à

. Cependant, je dois souligner que le polymorphisme réel du sous-type est obtenu en mettant en œuvre des contrats définis par la construction d'interface, ce qui est une approche très courante. Cependant, rien ne peut vous empêcher d'être moins orthodoxe et d'obtenir le même résultat en changeant une interface déclarée comme un ensemble de méthodes abstraites (situées dans une classe abstraite). Si vous vous sentez risqué et que vous voulez faire ce contournement, vous pouvez reconstruire le contrat et la mise en œuvre correspondante comme suit: ApcCache

<?php namespace LibraryCache;

interface CacheInterface
{
    public function set($id, $data);
    public function get($id);
    public function delete($id);
    public function exists($id);
}
Copier après la connexion
Copier après la connexion
<?php namespace LibraryCache;

class FileCache implements CacheInterface
{
    const DEFAULT_CACHE_DIRECTORY = 'Cache/';
    private $cacheDir;

    public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) {
        $this->setCacheDir($cacheDir);
    }

    public function setCacheDir($cacheDir) {
        if (!is_dir($cacheDir)) {
            if (!mkdir($cacheDir, 0644)) {
                throw InvalidArgumentException('The cache directory is invalid.');
            }
        }
        $this->cacheDir = $cacheDir;
        return $this;
    }

    public function set($id, $data) {
        if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) {
            throw new RuntimeException("Unable to cache the data with ID '$id'.");
        }
        return $this;
    }

    public function get($id) {
        if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) {
            throw new RuntimeException("Unable to get the data with ID '$id'.");
        }
        return $data;
    }

    public function delete($id) {
        if (!@unlink($this->cacheDir . $id)) {
            throw new RuntimeException("Unable to delete the data with ID '$id'.");
        }
        return $this;
    }

    public function exists($id) {
        return file_exists($this->cacheDir . $id);
    }
}
Copier après la connexion
Copier après la connexion
<?php namespace LibraryCache;

class ApcCache implements CacheInterface
{
    public function set($id, $data, $lifeTime = 0) {
        if (!apc_store($id, $data, (int) $lifeTime)) {
            throw new RuntimeException("Unable to cache the data with ID '$id'.");
        }
    }

    public function get($id) {
        if (!$data = apc_fetch($id)) {
            throw new RuntimeException("Unable to get the data with ID '$id'.");
        } 
        return $data;
    }

    public function delete($id) {
        if (!apc_delete($id)) {
            throw new RuntimeException("Unable to delete the data with ID '$id'.");
        }
    }

    public function exists($id) {
        return apc_exists($id);
    }
}
Copier après la connexion
Copier après la connexion

de haut en bas, il s'agit en effet d'une approche polymorphe, qui est contre la méthode discutée précédemment. Personnellement, ce n'est que ma déclaration personnelle, je préfère utiliser des constructions d'interface pour définir des contrats et utiliser des classes abstraites uniquement lors de l'encapsulation des implémentations de chaudières partagées par plusieurs sous-types. Vous pouvez choisir la méthode qui convient le mieux à vos besoins. À ce stade, je pouvais déposer le rideau, écrire des commentaires de fin de fantaisie, me vanter de nos compétences de codage impressionnantes et se vanter de la flexibilité de nos composants de cache, mais ce serait un affaissement. Lorsqu'il existe du code client qui peut consommer plusieurs implémentations, les polymorphismes présentent ses aspects les plus tentants sans vérifier si ces implémentations sont des instances d'un certain type, tant qu'elles répondent au contrat attendu. Revordons donc l'aspect en connectant le composant de cache à une classe de vision du client de base, qui nous permettra de faire une mise en cache HTML soigneusement sans effort.

Mettez le pilote de cache dans l'utilisation

La sortie HTML de mise en cache via notre exemple de module de cache est très simple et j'enregistrerai toutes les longues explications à d'autres moments. L'ensemble du processus de cache peut être simplifié en une classe de vue simple, similaire à ce qui suit:

<?php namespace LibraryCache;

abstract class AbstractCache
{
    abstract public function set($id, $data);
    abstract public function get($id);
    abstract public function delete($id);
    abstract public function exists($id);
}
Copier après la connexion
<?php namespace LibraryCache;

class FileCache extends AbstractCache
{
    // the same implementation goes here
}
Copier après la connexion
Le gars le plus éblouissant est le constructeur de classe, qui utilise les premiers implémentateurs de

et la méthode CacheInterface. Étant donné que la responsabilité de la dernière méthode est de mettre en cache le modèle de la vue après avoir été poussé vers le tampon de sortie, il serait bien de profiter de cette capacité et de mettre en cache l'ensemble du document HTML. Supposons que le modèle par défaut de la vue a la structure suivante: render()

<?php namespace LibraryCache;

class ApcCache extends AbstractCache
{
    // the same implementation goes here 
}
Copier après la connexion
Maintenant, nous amusons un peu et cachez le document en fournissant une instance de la classe

à la vue: ApcCache

<?php namespace LibraryView;

interface ViewInterface
{
    public function setTemplate($template);
    public function __set($field, $value);
    public function __get($field);
    public function render();
}
Copier après la connexion
C'est très bien, non? Mais attendez! J'étais tellement excité que j'ai oublié de mentionner que l'extrait de code ci-dessus exploserait sur un système qui n'a pas l'installation de l'extension APC (Naughty System Administrator!). Cela signifie-t-il que le module de cache soigneusement fabriqué n'est plus réutilisable? C'est exactement là que le pilote basé sur les fichiers entre en jeu, qui peut être placé dans le code client sans recevoir de plaintes:

<?php namespace LibraryView;
use LibraryCacheCacheInterface;

class View implements ViewInterface
{
    const DEFAULT_TEMPLATE = 'default';    
    private $template;
    private $fields = array();
    private $cache;

    public function __construct(CacheInterface $cache, $template = self::DEFAULT_TEMPLATE) {
        $this->cache = $cache;
        $this->setTemplate($template);
    }

    public function setTemplate($template) {
        $template = $template . '.php';
        if (!is_file($template) || !is_readable($template)) {
            throw new InvalidArgumentException(
                "The template '$template' is invalid.");   
        }
        $this->template = $template;
        return $this;
    }

    public function __set($name, $value) {
        $this->fields[$name] = $value;
        return $this;
    }

    public function __get($name) {
        if (!isset($this->fields[$name])) {
            throw new InvalidArgumentException(
                "Unable to get the field '$field'.");
        }
        return $this->fields[$name];
    }

    public function render() {
        try {
            if (!$this->cache->exists($this->template)) {
                extract($this->fields);
                ob_start();
                include $this->template;
                $this->cache->set($this->template, ob_get_clean());
            }
            return $this->cache->get($this->template);
        }
        catch (RuntimeException $e) {
            throw new Exception($e->getMessage());
        } 
    }
}
Copier après la connexion
La ligne de code unique ci-dessus indique explicitement que la vue utilisera le système de fichiers au lieu de la mémoire partagée pour mettre en cache sa sortie. Ce backend de cache de commutation dynamique illustre brièvement pourquoi le polymorphisme est si important lors de la conception de modules hautement découplés. Il nous permet de reconnecter facilement les choses à l'exécution sans répartir les artefacts liés à la vulnérabilité / rigidité à d'autres parties de notre système.

Conclusion Le polymorphisme

est en effet l'une de ces bonnes choses dans la vie, et une fois que vous la comprenez, cela vous fait vous demander comment vous pouvez faire sans que son cas se poursuit si longtemps. Les systèmes polymorphes sont intrinsèquement plus orthogonaux, plus faciles à évoluer et moins enclins à violer les paradigmes de base tels que le principe ouvert / fermé et le principe sage de la «programmation orientée vers l'interface». Bien que plutôt primitif, notre module de cache est un exemple important de ces avantages. Si vous n'avez pas refactorisé votre application pour profiter des avantages du polymorphisme, vous feriez mieux de vous dépêcher parce que vous avez manqué le jackpot! images de Fotolia

FAQs sur les polymorphismes du sous-type (FAQ)

Quelles sont les principales différences entre les polymorphismes de sous-type et les polymorphismes des paramètres?

Le polymorphisme de sous-type

, également connu sous le nom de polymorphisme d'inclusion, est une forme de polymorphisme dans laquelle un nom représente les instances de nombreuses catégories différentes associées à une superclasse publique. Le polymorphisme des paramètres, en revanche, permet à une fonction ou un type de données de traiter une valeur de la même manière sans compter sur son type. Le polymorphisme des paramètres est un moyen de rendre une langue plus expressive tout en maintenant une sécurité complète de type statique.

Comment fonctionne le polymorphisme des sous-types en Java?

En Java, le polymorphisme du sous-type est obtenu en utilisant l'héritage et les interfaces. Les variables de référence de superclasse peuvent pointer vers les objets sous-classe. Cela permet à Java de décider de la méthode à appeler lors de l'exécution, qui est appelée planification dynamique de la méthode. C'est l'une des caractéristiques puissantes de Java qui lui permet de prendre en charge le polymorphisme dynamique.

Pouvez-vous fournir un exemple de polymorphisme de sous-type?

Bien sûr, considérons un exemple simple de Java. Supposons que nous ayons une superclasse appelée "animal" et deux sous-classes "chien" et "chat". Les classes "Dog" et "Cat" réécrivent la méthode "Sound" de la classe "Animal". Maintenant, si nous créons une référence "Animal" pointant vers un objet "chien" ou "chat" et appelez la méthode "Sound", Java décidera à la méthode "son" de la classe à appeler. Ceci est un exemple de polymorphisme du sous-type.

Quelle est la signification du polymorphisme du sous-type dans la programmation?

Le polymorphisme

sous-type est un aspect fondamental de la programmation orientée objet. Il permet la flexibilité et la réutilisabilité du code. En utilisant le polymorphisme du sous-type, vous pouvez concevoir une interface commune pour un ensemble de classes, puis utiliser cette interface pour interagir avec les objets de ces classes de manière unifiée. Cela se traduira par un code plus propre, plus intuitif et plus facile à maintenir.

Quelle est la relation entre le polymorphisme du sous-type et le principe de remplacement de Liskov?

Le principe de substitution Liskov (LSP) est un principe de conception orientée objet qui indique que si un programme utilise une classe de base, il devrait être en mesure d'utiliser l'une de ses sous-classes sans que le programme le sache. En d'autres termes, les objets de superclasses devraient pouvoir être remplacés par des objets de sous-classes sans affecter l'exactitude du programme. Le polymorphisme du sous-type est une application directe de LSP.

Tous les langages de programmation prennent-ils en charge le polymorphisme du sous-type?

Non, tous les langages de programmation ne prennent pas en charge le polymorphisme du sous-type. Il s'agit principalement d'une caractéristique des langages de programmation orientés objet typés tels que Java, C et C #. Les langages dynamiquement typés comme Python et JavaScript ont différentes formes de polymorphisme, appelées types de canards.

Quelle est la différence entre le polymorphisme statique et le polymorphisme dynamique?

Le polymorphisme statique, également connu sous le nom de polymorphisme à temps de compilation, est réalisé grâce à la surcharge de méthode. La décision sur la méthode à laquelle appeler est prise au moment de la compilation. D'un autre côté, le polymorphisme dynamique, également connu sous le nom de polymorphisme d'exécution, est implémenté par la réécriture de la méthode. La décision sur la méthode à laquelle appeler est prise lors de l'exécution. Le polymorphisme du sous-type est un polymorphisme dynamique.

pouvez-vous expliquer le concept de conversion positive dans le polymorphisme du sous-type?

La conversion positive est un processus de traitement des objets de classe dérivés comme des objets de classe de base. C'est un aspect clé du polymorphisme du sous-type. Lorsque vous convertissez à la hausse un objet de classe dérivé, vous pouvez appeler n'importe quelle méthode définie dans la classe de base. Cependant, si la méthode est réécrite dans la classe dérivée, la version de réécriture sera appelée.

Qu'est-ce que la conversion en baisse dans le contexte du polymorphisme du sous-type?

La conversion vers le bas est l'opposé de la conversion UP. C'est le processus de conversion d'objets superclass en sous-classes. Lorsque vous devez accéder aux méthodes qui n'existent que dans les sous-classes, vous pouvez utiliser la conversion. Cependant, la conversion des bas peut être dangereuse car elle peut provoquer une convertie ClassCastException si l'objet converti n'a pas réellement le type auquel vous convertiez.

Comment le polymorphisme du sous-type favorise-t-il la réutilisabilité du code?

Le polymorphisme du sous-type nous permet d'écrire du code plus général et réutilisable. En utilisant des références de superclasse pour interagir avec les objets de sous-classe, nous pouvons écrire du code pour divers objets tant qu'ils appartiennent tous à des sous-classes de la même superclasse. Cela signifie que nous pouvons ajouter de nouvelles sous-classes sans modifier le code qui utilise des superclasses, ce qui rend notre code plus flexible et plus facile à entretenir.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal