Kernpunkte
Obwohl es den Spezifikationen nicht vollständig entspricht, kann gesagt werden, dass Orthogonalität die Essenz eines Softwaresystems basiert, das auf dem Prinzip des "guten Designs" basiert, in dem die Module voneinander entkoppelt sind, was das System weniger macht Anfällig für starre und fragile Probleme. Natürlich ist es einfacher, über die Vorteile orthogonaler Systeme zu sprechen, als Produktionssysteme zu betreiben. Trotzdem ist es keineswegs ein utopisches Konzept, stark entkoppelte Komponenten in einem System zu erreichen. Verschiedene Programmierkonzepte wie Polymorphismus ermöglichen die Gestaltung flexibler Programme, deren Teile zur Laufzeit umgeschaltet werden können, und ihre Abhängigkeiten können in abstrakten Formen und nicht in konkreten Implementierungen ausgedrückt werden. Ich würde sagen, dass das alte Motto der „interface-orientierten Programmierung“ im Laufe der Zeit weit verbreitet wurde, unabhängig davon, ob wir Infrastruktur oder Anwendungslogik implementieren. Wenn Sie jedoch in den Bereich der Domänenmodelle eintreten, ist die Situation sehr unterschiedlich. Ehrlich gesagt ist dies ein vorhersehbares Szenario. Warum sollte ein miteinander verbundenes Netzwerk von Objekten (die Daten und Verhaltensweisen, die durch genau definierte Geschäftsregeln begrenzt sind) polymorph sein? Es macht an sich nicht viel Sinn. Es gibt jedoch einige Ausnahmen von dieser Regel, die möglicherweise für diese Situation gelten. Die erste Ausnahme ist die Verwendung eines virtuellen Proxy, der praktisch die gleiche Schnittstelle ist wie das tatsächliche Domänenobjekt. Eine weitere Ausnahme ist der sogenannte "Null-Fall", bei dem es sich um einen Sonderfall handelt, in dem ein Betrieb Nullwerte zuweist oder zurückgibt, anstatt eine gut gefüllte Entität auszufüllen. Bei herkömmlichen nicht polymorphen Ansätzen muss der Benutzer des Modells diese "schädlichen" Nullwerte überprüfen und den Zustand anmutig behandeln, was zu einer Explosion bedingter Aussagen im gesamten Code führt. Glücklicherweise kann diese scheinbar verwirrende Situation leicht gelöst werden, indem einfach eine Multi-Branch-Implementierung des Domänenobjekts erstellt wird, das dieselbe Schnittstelle wie das betreffende Objekt implementiert, außer dass seine Methode nichts tut Überprüfen Sie wiederholt hässliche Nullwerte bei der Durchführung einer Operation. Es ist nicht überraschend, dass dieser Ansatz ein Entwurfsmuster ist, das als leere Objekte bezeichnet wird und die Vorteile des Polymorphismus auf das Extrem bringt. In diesem Artikel werde ich in mehreren Fällen die Vorteile dieses Musters demonstrieren und Ihnen zeigen, wie eng mit polymorphen Methoden verbunden ist.
Nicht-polymorphe Erkrankungen behandeln
Wie es erwarten würde, gibt es verschiedene Möglichkeiten, die Vorteile leerer Objektmuster anzuzeigen. Ein besonders einfacher Ansatz, den ich gefunden habe, ist die Implementierung eines Data Mapper, der möglicherweise Nullwerte von einem allgemeinen Finder zurückgibt. Angenommen, wir haben erfolgreich ein Skelett -Domänenmodell erstellt, das nur aus einer Benutzereinheit besteht. Die Schnittstelle und ihre Klasse sind wie folgt:
<?php namespace Model; interface UserInterface { public function setId($id); public function getId(); public function setName($name); public function getName(); public function setEmail($email); public function getEmail(); }
<?php namespace Model; class User implements UserInterface { private $id; private $name; private $email; public function __construct($name, $email) { $this->setName($name); $this->setEmail($email); } public function setId($id) { if ($this->id !== null) { throw new BadMethodCallException( "The ID for this user has been set already."); } if (!is_int($id) || $id throw new InvalidArgumentException( "The ID for this user is invalid."); } $this->id = $id; return $this; } public function getId() { return $this->id; } public function setName($name) { if (strlen($name) 30) { throw new InvalidArgumentException( "The user name is invalid."); } $this->name = $name; return $this; } public function getName() { return $this->name; } public function setEmail($email) { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException( "The user email is invalid."); } $this->email = $email; return $this; } public function getEmail() { return $this->email; } }
Die Benutzerklasse ist eine reaktive Struktur, die einige Mutatoren/Zubehör implementiert, um einige Benutzerdaten und Verhaltensweisen zu definieren. Mit dieser konstruierten Domänenklasse können wir nun noch einen Schritt weiter gehen und einen grundlegenden Daten Mapper definieren, der unser Domänenmodell und Datenzugriffsschicht voneinander isoliert.
<?php namespace ModelMapper; use LibraryDatabaseDatabaseAdapterInterface, ModelUser; class UserMapper implements UserMapperInterface { private $adapter; public function __construct(DatabaseAdapterInterface $adapter) { $this->adapter = $adapter; } public function fetchById($id) { $this->adapter->select("users", array("id" => $id)); if (!$row = $this->adapter->fetch()) { return null; } return $this->createUser($row); } private function createUser(array $row) { $user = new User($row["name"], $row["email"]); $user->setId($row["id"]); return $user; } }
Das erste, was ansehen sollte, ist, dass die Methode des Mapper fetchbyid () im Block eine ungezogene Methode ist, da sie null zurückgibt, wenn kein Benutzer in der Datenbank mit der angegebenen ID übereinstimmt. Aus offensichtlichen Gründen muss der Client -Code bei dieser ungeschickten Bedingung hart arbeiten, um den Nullwert jedes Mal zu überprüfen, wenn er den Finder des Mapper aufruft.
<?php use LibraryLoaderAutoloader, LibraryDatabasePdoAdapter, ModelMapperUserMapper; require_once __DIR__ . "/Library/Loader/Autoloader.php"; $autoloader = new Autoloader; $autoloader->register(); $adapter = new PdoAdapter("mysql:dbname=test", "myusername", "mypassword"); $userMapper = new UserMapper($adapter); $user = $userMapper->fetchById(1); if ($user !== null) { echo $user->getName() . " " . $user->getEmail(); }
Auf den ersten Blick wird es kein Problem geben. Überprüfen Sie es einfach an einem Ort. Wenn dieselbe Linie jedoch in mehreren Seitencontrollern oder Servicelegoren erscheint, stoßen Sie dann in eine Mauer? Bevor Sie es erkennen, wird der scheinbar unschuldige Null, der vom Mapper zurückgegeben wird, viele sich wiederholende Bedingungen erzeugen, was ein schlechtes Omen von schlechtem Design ist.
Entfernen Sie Bedingungsanweisungen aus dem Client -Code
Es besteht jedoch keine Notwendigkeit, sich Sorgen zu machen, da dies genau der Fall ist, in dem das leere Objektmuster zeigt, warum Polymorphismus ein Glücksfall ist. Wenn wir diese nervigen bedingten Aussagen ein für alle Mal loswerden möchten, können wir die polymorphe Version der vorherigen Benutzerklasse implementieren.
<?php namespace Model; class NullUser implements UserInterface { public function setId($id) { } public function getId() { } public function setName($name) { } public function getName() { } public function setEmail($email) { } public function getEmail() { } }
Wenn Sie erwarten, dass eine vollständige Entitätsklasse alle Arten von Dekorationen verpackt, sind Sie wahrscheinlich sehr enttäuscht. Die "Null" -Version der Entität entspricht der entsprechenden Schnittstelle, aber die Methode ist ein leerer Wrapper und es gibt keine tatsächliche Implementierung. Die Existenz der Nulluser -Klasse bringt uns zwar offensichtlich nichts, was Lob verdient, aber es ist eine kurze Struktur, die es uns ermöglicht, alle früheren bedingten Aussagen in den Müll zu werfen. Möchten Sie sehen, wie es implementiert wird? Zunächst sollten wir einige Vorarbeiten durchführen und den Data Mapper rekonstruieren, damit sein Finder ein leeres Benutzerobjekt anstelle eines leeren Werts zurückgibt.
<?php namespace ModelMapper; use LibraryDatabaseDatabaseAdapterInterface, ModelUser, ModelNullUser; class UserMapper implements UserMapperInterface { private $adapter; public function __construct(DatabaseAdapterInterface $adapter) { $this->adapter = $adapter; } public function fetchById($id) { $this->adapter->select("users", array("id" => $id)); return $this->createUser($this->adapter->fetch()); } private function createUser($row) { if (!$row) { return new NullUser; } $user = new User($row["name"], $row["email"]); $user->setId($row["id"]); return $user; } }
<?php namespace Model; interface UserInterface { public function setId($id); public function getId(); public function setName($name); public function getName(); public function setEmail($email); public function getEmail(); }
Der Hauptnachteil dieses polymorphen Ansatzes besteht darin, dass jede Anwendung, die sie verwendet, zu locker wird, weil sie beim Umgang mit ungültigen Entitäten nie abstürzt. Im schlimmsten Fall zeigt die Benutzeroberfläche nur einige leere Zeilen an, aber nichts wirklich lautes macht uns angewidert. Dies ist besonders offensichtlich, wenn Sie nach der aktuellen Implementierung der frühen Nulluser -Klasse scannen. Auch wenn es machbar ist, ganz zu schweigen von dem empfohlenen, kann es auch die Logik in leeren Objekten zusammenschließen, während der Polymorphismus unverändert bleibt. Ich kann sogar sagen, dass leere Objekte perfekt für die Einkapselung von Standarddaten und Verhaltensweisen sind, die nur in wenigen Sonderfällen dem Client -Code ausgesetzt sein sollten. Wenn Sie ehrgeizig genug sind und dieses Konzept mit einem einfachen leeren Benutzerobjekt ausprobieren möchten, kann die aktuelle Nulluser -Klasse wie folgt überarbeitet werden:
<?php namespace Model; class User implements UserInterface { private $id; private $name; private $email; public function __construct($name, $email) { $this->setName($name); $this->setEmail($email); } public function setId($id) { if ($this->id !== null) { throw new BadMethodCallException( "The ID for this user has been set already."); } if (!is_int($id) || $id throw new InvalidArgumentException( "The ID for this user is invalid."); } $this->id = $id; return $this; } public function getId() { return $this->id; } public function setName($name) { if (strlen($name) 30) { throw new InvalidArgumentException( "The user name is invalid."); } $this->name = $name; return $this; } public function getName() { return $this->name; } public function setEmail($email) { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException( "The user email is invalid."); } $this->email = $email; return $this; } public function getEmail() { return $this->email; } }
Die erweiterte Version von Nulluser ist etwas ausdrucksvoller als der ruhige Vorgänger, da der Getter einige grundlegende Implementierungen bereitstellt, um einige Standardnachrichten zurückzugeben, wenn sie einen ungültigen Benutzer anfordern. Diese Änderung hat zwar trivial, hat sich positiv darauf ausgewirkt, wie Clientcode leeren Benutzern umgeht, da diesmal Benutzer zumindest wissen, dass einige Probleme auftreten, wenn sie versuchen, nicht existierende Benutzer aus dem Speicher zu extrahieren. Dies ist ein guter Durchbruch, der nicht nur zeigt, wie leere Objekte implementiert werden, die überhaupt nicht leer sind, sondern auch, wie einfach es ist, die Logik in relevanten Objekten basierend auf bestimmten Anforderungen zu verschieben.
Schlussfolgerung
Einige könnten sagen, dass die Implementierung leerer Objekte problematisch ist, insbesondere in PHP, wo Kernkonzepte von OOP (wie Polymorphismus) signifikant unterschätzt werden. Sie haben bis zu einem gewissen Grad Recht. Die allmähliche Einführung vertrauenswürdiger Programmierprinzipien und Designmuster sowie die Anzahl der Reife, die das Sprachobjektmodell erreicht hat Vor nicht allzu langer Zeit bildet alle notwendigen Grundlagen. Das Null -Objektmuster fällt in diese Kategorie, aber seine Implementierung ist so einfach und elegant, dass es schwierig ist, es nicht attraktiv zu finden, wenn du doppelte Nullprüfungen im Client -Code löschst. Bilder aus Fotolien
(Aus Platzbeschränkungen wird der FAQ -Teil im Originaltext hier weggelassen.)
Das obige ist der detaillierte Inhalt vonPHP Master | Das Null -Objektmuster - Polymorphismus in Domänenmodellen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!