Points de base
Scène fictive: pirate et matrice
La conversation suivante vient d'une scène coupée de la trilogie matricielle:
Merpheus: Neo, je suis dans la matrice maintenant. Désolé de vous dire cette mauvaise nouvelle, mais notre programme PHP de suivi des agents a besoin d'une mise à jour rapide. Il utilise actuellement la méthode Query () de PDO (avec String) pour obtenir l'état de tous les agents matriciels de notre base de données, mais nous devons utiliser des requêtes de prétraitement à la place.
neo: sonne bien, Morpheus. Puis-je obtenir une copie du programme?
Merphes: pas de problème. Clone notre référentiel et consultez les fichiers agentmapper.php et index.php.
(Neo exécute certaines commandes Git, et le code suivant apparaît devant lui)
<?php namespace ModelMapper; class AgentMapper { protected $_adapter; protected $_table = "agents"; public function __construct(PDO $adapter) { $this->_adapter = $adapter; } public function findAll() { try { return $this->_adapter->query("SELECT * FROM " . $this->_table, PDO::FETCH_OBJ); } catch (Exception $e) { return array(); } } }
<?php use ModelMapperAgentMapper; // 一个 PSR-0 兼容的类加载器 require_once __DIR__ . "/Autoloader.php"; $autoloader = new Autoloader(); $autoloader->register(); $adapter = new PDO("mysql:dbname=Nebuchadnezzar", "morpheus", "aa26d7c557296a4e8d49b42c8615233a3443036d"); $agentMapper = new AgentMapper($adapter); $agents = $agentMapper->findAll(); foreach ($agents as $agent) { echo "Name: " . $agent->name . " - Status: " . $agent->status . "<br>"; }
Nio: Morpheus, je viens de recevoir les documents. J'ai sous-classé APD et remplacer sa méthode Query () afin qu'il puisse utiliser des requêtes prétraitées. En raison de mes superpuissances, je devrais pouvoir terminer ce travail très rapidement. reste calme.
(Le son du clavier de l'ordinateur résonne dans l'air)
Nio: Morpheus, la sous-classe est prête pour les tests. Vérifiez-le à tout moment.
(Murphys a fouillé rapidement sur son ordinateur portable et a vu la classe suivante)
<?php namespace LibraryDatabase; class PdoAdapter extends PDO { protected $_statement; public function __construct($dsn, $username = null, $password = null, array $driverOptions = array()) { // 检查是否传递了有效的 DSN if (!is_string($dsn) || empty($dsn)) { throw new InvalidArgumentException("The DSN must be a non-empty string."); } try { // 尝试创建一个有效的 PDO 对象并设置一些属性。 parent::__construct($dsn, $username, $password, $driverOptions); $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); } catch (PDOException $e) { throw new RunTimeException($e->getMessage()); } } public function query($sql, array $parameters = array()) { try { $this->_statement = $this->prepare($sql); $this->_statement->execute($parameters); return $this->_statement->fetchAll(PDO::FETCH_OBJ); } catch (PDOException $e) { throw new RunTimeException($e->getMessage()); } } }
Merphes: L'adaptateur a l'air bien. Je vais l'essayer tout de suite pour voir si notre agent Mapper est capable de suivre les agents actifs qui voyagent à travers la matrice. Bonne chance à moi.
(Murphys a hésité un instant, exécutant le fichier index.php précédent, cette fois en utilisant la classe PdoAdapter du chef-d'œuvre de Neo. Ensuite, un cri!)
Merpheus: Neo, je crois que vous êtes le "Sauveur"! Mais il y avait une terrible erreur fatale sur mon visage, et la nouvelle était la suivante:
<code>Catchable fatal error: Argument 2 passed to LibraryDatabasePdoAdapter::query() must be an array, integer given, called in path/to/AgentMapper on line (who cares?)</code>
(un autre cri)
NEO: Qu'est-ce qui ne va pas? ! Qu'est-ce qui ne va pas? ! (plus de cris)
Merphes: Je ne sais vraiment pas. Oh, l'agent Smith vient me rattraper maintenant! (La communication a été soudainement interrompue. Le long silence a mis fin à la conversation, suggérant que Morpheus a été pris au dépourvu et a été gravement blessé par l'agent Smith.)
LSP ne représente pas les programmeurs paresseux et stupides
Inutile de dire que le dialogue ci-dessus est fictif, mais le problème est sans aucun doute vrai. Si Neo n'avait appris qu'une ou deux connaissances sur le principe de substitution de Liskov (LSP) comme un pirate qu'il était aussi célèbre qu'autrefois, l'agent Smith pouvait être retracé immédiatement. Plus important encore, Morpheus est protégé de la méchanceté de l'agent. C'était tellement dommage pour lui. Cependant, dans de nombreux cas, les développeurs de PHP pensent à LSP presque le même que l'opinion précédente de NEO: LSP n'est rien d'autre qu'un principe théorique puriste qui a peu d'application dans la pratique. Mais ils sont allés dans le mauvais sens. Même si la définition formelle du LSP est éblouissante (y compris moi), son noyau est d'éviter une hiérarchie de classes de façon irrégulière où les descendants se comportent très différemment des abstractions de classe de base qui utilisent le même contrat. En termes simples, LSP stipule que lors de la réécriture des méthodes dans les sous-classes, les exigences suivantes doivent être satisfaites:
Maintenant, n'hésitez pas à relire la liste par-dessus (ne vous inquiétez pas, j'attendrai) et vous espérez comprendre pourquoi cela a du sens. Pour en revenir à l'exemple, l'erreur fatale de Neo ne parvient pas à maintenir la signature de la méthode la même, brisant ainsi le contrat avec le code client. Pour résoudre ce problème, la méthode findall () du mappeur d'agent peut être réécrite avec certaines instructions conditionnelles (odeur de code évidente), comme indiqué ci-dessous:
<?php namespace ModelMapper; class AgentMapper { protected $_adapter; protected $_table = "agents"; public function __construct(PDO $adapter) { $this->_adapter = $adapter; } public function findAll() { try { return $this->_adapter->query("SELECT * FROM " . $this->_table, PDO::FETCH_OBJ); } catch (Exception $e) { return array(); } } }
Si vous êtes de bonne humeur, essayez la méthode de refactorisation et cela fonctionnera bien, que ce soit en utilisant un objet PDO natif ou une instance d'un adaptateur PDO. Je sais que cela semble rugueux, mais c'est juste une solution rapide et facile, et cela viole manifestement le principe de l'ouverture et de la fermeture. D'un autre côté, la méthode Query () de l'adaptateur peut être refactorisée pour correspondre à sa signature de la classe parent de réécriture. Mais ce faisant, toutes les autres conditions énoncées par le LSP doivent également être satisfaites. En bref, cela signifie que la réécriture de la méthode doit être effectuée avec prudence et ne peut être effectuée que pour des raisons très solides. Dans de nombreux cas d'utilisation, en supposant que l'interface ne peut pas être utilisée, il est préférable de créer une sous-classe qui ne fait que s'étendre (plutôt que de remplacer) sa fonctionnalité de classe de base. Dans le cas de l'adaptateur APD de Neo, cette approche fonctionnera parfaitement et ne cassera jamais le code client à aucun niveau. Comme je viens de le dire, il y a une solution plus efficace - mais plus radicale - qui profite de la mise en œuvre d'interfaces. Alors que les adaptateurs PDO précédents ont été créés par l'héritage et violaient indéniablement les préceptes du LSP, les inconvénients proviennent en fait de la façon dont la classe de mappeur de l'agent a été conçue à l'origine. En fait, cela dépend d'une implémentation d'adaptateur de base de données concrète de haut en bas, plutôt que d'un contrat défini par l'interface. Et une grande puissance OO a été dite depuis les temps anciens, c'est toujours une mauvaise chose. Alors, comment la solution ci-dessus sera-t-elle implémentée?
(le reste est similaire au texte d'entrée, et peut être ajusté et simplifié au besoin)
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!