Points de base
La programmation logicielle est une combinaison équilibrée d'art (parfois l'euphémisme pour l'improvisation) et de nombreuses heuristiques éprouvées pour résoudre certains problèmes et les résoudre de manière décente. Peu de gens ne sont pas d'accord que l'aspect artistique est de loin le plus difficile à affiner et à affiner. D'un autre côté, la puissance derrière l'heuristique est essentielle pour pouvoir développer des logiciels basés sur une bonne conception. Avec autant d'heuristiques expliquant comment et pourquoi les systèmes logiciels devraient s'en tenir à des méthodes spécifiques, il est tout à fait décevant de ne pas les voir plus largement implémentés dans le monde PHP. Par exemple, la loi dimitienne peut être l'une des lois les plus sous-estimées dans le domaine des langues. En fait, la devise de la règle de «parler uniquement avec vos amis proches» semble être dans un état plutôt immature en PHP, ce qui a entraîné une baisse de la qualité globale de plusieurs bases de code orientées objet. Certains cadres populaires le poussent activement dans le but d'adhérer davantage aux préceptes de la loi. Cela n'a aucun sens de se blâmer pour avoir violé la loi dimitaise, car la meilleure façon d'atténuer une telle destruction est de simplement adopter une attitude pragmatique et de comprendre ce qui est réellement sous la loi, afin qu'il soit consciemment appliqué lors de l'écriture de code orienté objet . Pour rejoindre la cause de la justice et étudier la loi plus profondément d'un point de vue pratique, dans les prochaines lignes, je démontrerai avec quelques exemples pratiques pour lesquels les choses aussi simples que l'observation des principes de la loi sont conçues dans un module logiciel à couplage vaguement couplé Le temps peut vraiment améliorer l'efficacité.
Ce n'est pas une bonne chose de savoir trop
est souvent appelé le principe de la moindre connaissance, et les règles préconisées par la loi dimitaise sont faciles à comprendre. Autrement dit, supposons que vous ayez une classe bien conçue qui implémente une méthode donnée, alors la méthode doit être limitée à appeler d'autres méthodes qui appartiennent à l'objet suivant:
Bien que la liste soit loin d'être formelle (pour des listes plus formelles, consultez Wikipedia), ces points clés sont faciles à comprendre. Dans la conception traditionnelle, il est considéré comme mal d'en savoir trop sur un autre objet (qui comprend implicitement savoir comment accéder à un troisième objet) car dans certains cas, l'objet doit aller inutilement de haut en bas en itérant à travers les intermédiaires maladroits pour trouver les dépendances réelles il doit fonctionner comme prévu. Il s'agit d'un défaut de conception sérieux pour des raisons évidentes. L'appelant a une compréhension assez étendue et détaillée de la structure interne de l'intermédiaire, même si celle-ci est accessible via plusieurs getters. De plus, l'utilisation d'objets intermédiaires pour obtenir l'objet requis par l'appelant illustre un problème en soi. Après tout, si le même résultat peut être obtenu en injectant directement les dépendances, pourquoi utiliser un chemin aussi complexe pour obtenir les dépendances ou appeler l'une de ses méthodes? Ce processus n'a aucun sens.
Supposons que nous devons créer un module de stockage de fichiers qui utilise un encodeur polymorphe en interne pour extraire des données et les enregistrer dans le fichier cible donné. Si nous connectons délibérément le module à un localisateur de service injectable, son implémentation ressemblera à ceci:
<?php namespace LibraryFile; use LibraryDependencyInjectionServiceLocatorInterface; class FileStorage { const DEFAULT_STORAGE_FILE = "data.dat"; private $locator; private $file; public function __construct(ServiceLocatorInterface $locator, $file = self::DEFAULT_STORAGE_FILE) { $this->locator = $locator; $this->setFile($file); } public function setFile($file) { if (!is_readable($file) || !is_writable($file)) { throw new InvalidArgumentException( "The target file is invalid."); } $this->file = $file; return $this; } public function write($data) { try { return file_put_contents($this->file, $this->locator->get("encoder")->encode($data), LOCK_EX); } catch (Exception $e) { throw new $e( "Error writing data to the target file: " . $e->getMessage()); } } public function read() { try { return $this->locator->get("encoder")->decode( @file_get_contents($this->file)); } catch(Exception $e) { throw new $e( "Error reading data from the target file: " . $e->getMessage()); } } }
omettre certains détails de mise en œuvre non pertinents, en se concentrant sur le constructeur de la classe FileStorage et ses méthodes écrites () et read (). Cette classe injecte une instance d'un localisateur de service qui n'a pas été définie et est plus tard utilisée pour récupérer les dépendances (l'encodeur susmentionné) afin de récupérer et de stocker des données dans le fichier cible. Il s'agit généralement d'une violation de la loi de dimingants, étant donné que la classe traverse d'abord le localisateur, puis atteint le codeur. L'appelant FileStorage en sait trop sur la structure interne du localisateur, y compris comment accéder au codeur, ce qui n'est certainement pas une capacité que je voudrais louer. Il s'agit d'un artefact enraciné intrinsèquement dans la nature d'un localisateur de services (c'est pourquoi certaines personnes le considèrent comme anti-motif) ou tout autre type de registre statique ou dynamique, ce que j'ai signalé auparavant. Pour obtenir une compréhension plus complète de ce problème, vérifions la mise en œuvre du localisateur:
(Les codes de localisateur et de codeur sont omis ici car ils sont cohérents avec la sortie précédente. Afin d'éviter la duplication, je ne les répéterai pas ici.)
Avec l'encodeur, commençons par toutes les classes d'échantillons ensemble:
(l'exemple de code d'utilisation est omis ici car il est cohérent avec la sortie précédente. Afin d'éviter la duplication, je ne le répéterai pas ici.)
Les violations de cette loi sont un problème plutôt secret dans ce cas et sont difficiles à suivre depuis la surface, à l'exception de l'utilisation du mutateur du localisateur, ce qui suggère qu'à un moment donné, le codeur sera en quelque sorte pris par un accès d'instance FileStorage et utiliser. En tout cas, le fait que nous sachions que la violation est là cachée en dehors du monde extérieur révèle non seulement trop d'informations sur la structure des localisateurs, mais associe également inutilement la classe FileStorage au localisateur lui-même. Suivez simplement les règles de cette règle et débarrassez-vous du localisateur, nous pouvons supprimer le couplage tout en fournissant à FileStorage les collaborateurs réels dont il a besoin pour faire ses activités. Il n'y a plus d'intermédiaires maladroits et exposés en cours de route! Heureusement, toutes ces bêtises peuvent être facilement converties en code de travail avec un peu d'effort. Affichez simplement la version améliorée et conforme à la loi de Dimitri de la classe FileStorage ici:
(Le code FileStorage refactorisé est omis ici car il est cohérent avec la sortie précédente. Afin d'éviter la duplication, je ne le répéterai pas ici.)
C'est en effet facile à refactor. Désormais, la classe utilise directement n'importe quel implémentateur de l'interface EncodeRinterface, en évitant de traverser la structure interne des intermédiaires inutiles. L'exemple est sans aucun doute trivial, mais il illustre un point de validité et montre pourquoi suivre les préceptes de la loi de Dimitri est l'une des meilleures choses que vous puissiez faire pour améliorer la conception de la classe. Cependant, un cas particulier de cette règle est profondément exploré dans le livre de Robert Martin "The Way of Code: Agile Software Development Manual" et mérite une analyse spéciale. Veuillez prendre un moment pour réfléchir attentivement: que se passe-t-il si FileStorage est défini comme un collaborateur via un objet de transfert de données (DTO)?
(L'exemple de code utilisant DTO est omis ici, car il est cohérent avec la sortie précédente, et afin d'éviter la duplication, je ne le répéterai pas ici.)
Il s'agit certainement d'un moyen intéressant d'implémenter les classes de stockage de fichiers, car il utilise désormais des DTO injectables pour transférer et utiliser l'encodeur en interne. La question à laquelle il faut répondre est de savoir si cette méthode viole vraiment la loi. D'un point de vue puriste, il viole, car le DTO est sans aucun doute un intermédiaire qui expose toute sa structure à l'appelant. Cependant, le DTO n'est qu'une structure de données normale, contrairement aux localisateurs de services antérieurs, il ne se comporte pas du tout. Et le but de la structure de données est ... oui, de divulguer ses données. Cela signifie que tant que l'intermédiaire ne met pas en œuvre le comportement (qui est exactement l'opposé du comportement d'une classe régulière, qui masque ses données car elle expose le comportement), la loi de dimitter reste intacte. L'extrait de code suivant montre comment utiliser FileStorage en utilisant le DTO problématique:
(L'exemple de code utilisant DTO est omis ici, car il est cohérent avec la sortie précédente, et afin d'éviter la duplication, je ne le répéterai pas ici.)
Cette approche est beaucoup plus gênante que de passer un encodeur directement dans une classe de stockage de fichiers, mais l'exemple montre que certaines implémentations délicates qui peuvent apparaître à première vue comme des violations flagrantes de la loi, sont généralement assez inoffensives, tant que Ils utilisent simplement une structure de données sans aucun comportement supplémentaire.
Conclusion
En tant que divers complexes, parfois des heuristiques ésotériques sont populaires dans la POO, il semble dénué de sens d'ajouter un autre principe qui n'a évidemment aucun impact positif évident sur la conception des composants de la couche. Cependant, la loi Dimitter n'est en aucun cas un principe qui n'est guère appliqué dans le monde réel. Malgré son nom magnifique, la loi Dimitt est un puissant paradigme dans le but principal de faciliter la mise en œuvre de composants d'application hautement découplés en éliminant tout intermédiaire inutile. Suivez simplement ses préceptes et bien sûr, ne soyez pas aveuglément dogmatique et vous verrez une amélioration de la qualité du code. assurer.
(La partie FAQ est omise ici car elle est cohérente avec la sortie précédente. Afin d'éviter la duplication, je ne le répéterai pas ici.)
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!