Regeln für die Arbeit mit dynamischen Arrays und benutzerdefinierten Auflistungsklassen
Hier sind die Regeln, die ich beim Arbeiten mit dynamischen Arrays befolge. Tatsächlich ist dies eine Anleitung zum Entwerfen von Arrays, aber ich wollte sie nicht in eine Anleitung zum Entwerfen von Objekten aufnehmen, da nicht jede objektorientierte Sprache dynamische Arrays hat. Die Beispiele sind in PHP geschrieben, da sie Java ähneln (mit dem Sie möglicherweise bereits vertraut sind), aber mit dynamischen Arrays anstelle der integrierten Auflistungsklassen und -schnittstellen.
Verwenden von Arrays als Liste
Alle Artikel müssen vom selben Typ sein.
Wenn Sie ein Array als Liste verwenden (eine Sammlung von Werten in einer bestimmten Reihenfolge), müssen alle Werte vom gleichen Typ sein:
$goodList = [ 'a', 'b' ]; $badList = [ 'a', 1 ];
Der gebräuchliche Annotationsstil für
@var array<TypeOfElment>
lautet:
@var array<TypeOfElment>
. Stellen Sie sicher, dass Sie keinen Indextyp hinzufügen, er sollte immer
int
.
Der Index jedes Elements muss ignoriert werden
PHP erstellt automatisch einen neuen Index für jedes Listenelement (0, 1, 2 usw.). Sie sollten sich jedoch weder auf diese Indizes verlassen noch sie direkt verwenden. Kunden können sich nur auf
iterable
und
countable
.
So können Sie
foreach
und
count()
frei verwenden, aber nicht, um Listenelemente zu durchlaufen:
In PHP funktioniert die
for
Schleife möglicherweise überhaupt nicht, wenn die Liste keine Indizes enthält oder wenn mehr Indizes als die Anzahl der Elemente vorhanden sind.
Verwenden Sie einen Filter, anstatt Elemente zu löschen
Möglicherweise möchten Sie Elemente nach Index löschen (
unset()
), aber anstatt zu löschen, ist es besser, mit
array_filter()
eine neue Liste ohne unerwünschte Elemente zu erstellen.
Auch hier sollte man sich nicht auf Elementindizes verlassen. Verwenden
array_filter()
bei Verwendung von
array_filter()
nicht
den Parameter flag , um Elemente nach Index oder sogar nach Element und Index herauszufiltern.
Verwenden von Arrays als assoziative Arrays
Wenn die Schlüssel relevant sind und keine Indizes (0, 1, 2 usw.), verwenden Sie frei assoziative Arrays (eine Auflistung, aus der Sie Werte anhand ihrer eindeutigen Schlüssel extrahieren können).
Alle Schlüssel müssen vom selben Typ sein.
Die erste Regel für die Verwendung von assoziativen Arrays: Alle Schlüssel müssen vom gleichen Typ sein (meistens handelt es sich um
string
).
$goodMap = [ 'foo' => 'bar', 'bar' => 'baz' ];
Alle Werte müssen vom selben Typ sein.
Gleiches gilt für Werte: Sie müssen vom gleichen Typ sein.
$goodMap = [ 'foo' => 'bar', 'bar' => 'baz' ];
Ein gebräuchlicher Stil zum Kommentieren eines Typs ist:
@var array<TypeOfKy, TypeOfValue>
.
Assoziative Arrays müssen privat bleiben
Listen können aufgrund der Einfachheit ihrer Eigenschaften sicher von Objekt zu Objekt übertragen werden. Jeder Client kann Elemente durchlaufen oder zählen, auch wenn die Liste leer ist. Es ist schwieriger, mit Map zu arbeiten, da Clients sich auf Schlüssel verlassen können, die keinem Wert entsprechen. Dies bedeutet, dass assoziative Arrays in Bezug auf die Objekte, die sie verwalten, normalerweise privat bleiben sollten. Anstatt Clients den direkten Zugriff auf interne Zuordnungen zu ermöglichen, können Getter (und möglicherweise Setter) Werte abrufen. Ausnahmen auslösen, wenn für den angeforderten Schlüssel kein Wert vorhanden ist. Wenn Sie jedoch die Karte und ihre Werte vollständig geheim halten können, tun Sie dies.
Verwenden Sie Objekte als assoziative Arrays mit Werten verschiedener Typen
Wenn Sie ein assoziatives Array verwenden möchten, aber Werte unterschiedlichen Typs darin speichern möchten, verwenden Sie Objekte. Definieren Sie eine Klasse, fügen Sie öffentliche Typeneigenschaften hinzu oder fügen Sie einen Konstruktor und Getter hinzu. Solche Objekte umfassen Konfigurations- oder Befehlsobjekte:
final class SillyRegisterUserCommand { public string $username; public string $plainTextPassword; public bool $wantsToReceiveSpam; public int $answerToIAmNotARobotQuestion; }
Ausnahmen von der Regel
Bibliotheken und Frameworks erfordern manchmal eine dynamischere Verwendung von Arrays. Dann ist es unmöglich (und unerwünscht), die vorherigen Regeln zu befolgen. Beispiele sind das
Array von Daten , die in einer Datenbanktabelle gespeichert sind, und
die Formularkonfiguration in Symfony.
Benutzerdefinierte Sammlungsklassen
Benutzerdefinierte Auflistungsklassen können ein großartiges Werkzeug für die Arbeit mit
Iterator
,
ArrayAccess
und anderen Entitäten sein, aber ich finde, dass der Code oft verwirrend wird. Wer sich zum ersten Mal mit Code befasst, muss das PHP-Handbuch konsultieren, auch wenn er ein erfahrener Entwickler ist. Außerdem müssen Sie mehr Code schreiben, um zu warten (testen, debuggen usw.). In den meisten Fällen ist ein einfaches Array mit den richtigen Typanmerkungen ausreichend.
Was bedeutet, dass Sie das Array in ein benutzerdefiniertes Auflistungsobjekt einschließen müssen?
- Vervielfältigung der Logik in Bezug auf ein Array.
- Clients müssen mit zu vielen Details über den Inhalt des Arrays arbeiten.
Verwenden Sie eine benutzerdefinierte Auflistungsklasse, um doppelte Logik zu verhindern.
Wenn mehrere Clients, die mit demselben Array arbeiten, dieselbe Aufgabe ausführen (z. B. Filtern, Vergleichen, Reduzieren, Zählen), können Duplikate mithilfe der benutzerdefinierten Auflistungsklasse entfernt werden. Durch die Übertragung doppelter Logik in eine Sammlungsklassenmethode kann jeder Client dieselbe Aufgabe ausführen, indem er einfach die Sammlungsmethode aufruft:
$names = [];
Der Vorteil der Transformation einer Auflistung mithilfe einer Methode besteht darin, dass diese Transformation benannt wird. Sie können einen kurzen und informativen Namen für den Aufruf von
array_filter()
, der sonst ziemlich schwer zu finden wäre.
Binden Sie Kunden mit einer benutzerdefinierten Auflistungsklasse auf
Wenn ein Client ein Array durchläuft, einen Teil der Daten aus den ausgewählten Elementen entnimmt und etwas damit unternimmt, wird dieser Client eng mit allen entsprechenden Typen verknüpft: Array, Elemente, abgerufene Werte, Auswahlmethode usw. dass es aufgrund einer solch tiefen Bindung für Sie viel schwieriger sein wird, irgendetwas in Bezug auf diese Typen zu ändern, ohne den Client zu beschädigen. In diesem Fall können Sie das Array auch in eine benutzerdefinierte Auflistungsklasse einschließen und die richtige Antwort geben, die erforderlichen Berechnungen im Array ausführen und die Bindung des Clients an die Auflistung lösen.
$lines = []; $sum = 0; foreach ($lines as $line) { if ($line->isComment()) { continue; } $sum += $line->quantity(); }
Einige Regeln für benutzerdefinierte Auflistungsklassen
Mach sie unveränderlich
Bei der Durchführung solcher Transformationen sollten die vorhandenen Verweise auf die Auflistungsinstanz nicht betroffen sein. Daher sollte jede Methode, die diese Konvertierung ausführt, eine neue Instanz der Klasse zurückgeben, wie wir im vorherigen Beispiel gesehen haben:
final class Names { private array $names; public function __construct(array $names) { Assert::that()->allIsString($names); $this->names = $names; } public function shortNames(): self { return new self( ); } }
Wenn Sie ein internes Array konvertieren, können Sie natürlich in einen anderen Auflistungstyp oder ein einfaches Array konvertieren. Stellen Sie wie gewohnt sicher, dass der richtige Typ zurückgegeben wird.
Stellen Sie nur das Verhalten zur Verfügung, das Kunden wirklich benötigen
Anstatt eine Klasse aus einer Bibliothek mit einer universellen Sammlung zu erweitern oder einen universellen Filter oder eine universelle Zuordnung zu implementieren und für jede benutzerdefinierte Sammlungsklasse zu reduzieren, implementieren Sie nur das, was Sie wirklich benötigen. Wenn Sie die Methode irgendwann nicht mehr verwenden, löschen Sie sie.
Verwenden Sie IteratorAggregate und ArrayIterator zum Iterieren
Wenn Sie mit PHP arbeiten, implementieren Sie anstelle aller Methoden der
Iterator
Schnittstelle (Speichern interner Zeiger usw.) nur die
IteratorAggregate
Schnittstelle und lassen Sie sie eine
ArrayIterator
Instanz basierend auf dem internen Array zurückgeben:
final class Names implements IteratorAggregate { private array $names; public function __construct(array $names) { Assert::that()->allIsString($names); $this->names = $names; } public function getIterator(): Iterator { return new ArrayIterator($this->names); } } $names = new Names([]); foreach ($names as $name) {
Kompromiss
Da Sie mehr Code für eine benutzerdefinierte Auflistungsklasse schreiben, sollte es für Clients einfacher sein, mit dieser Auflistung (und nicht nur mit einem Array) zu arbeiten. Wenn der Clientcode klarer wird und die Auflistung nützliches Verhalten bietet, ist der zusätzliche Aufwand für die Verwaltung einer benutzerdefinierten Auflistungsklasse gerechtfertigt. Da die Arbeit mit dynamischen Arrays jedoch so einfach ist (hauptsächlich, weil Sie die verwendeten Typen nicht angeben müssen), verwende ich meine Auflistungsklassen selten. Einige Entwickler setzen sie jedoch aktiv ein, sodass ich auf jeden Fall weiter nach möglichen Anwendungsfällen suchen werde.