Heim > Artikel > Backend-Entwicklung > Array-Spezifikationen und benutzerdefinierte Sammlungen in PHP
Dies ist so ziemlich ein Styleguide für Array-Design, das Hinzufügen zum Object Design Style Guide jedoch nicht fühle mich richtig Ja, weil nicht alle objektorientierten Sprachen dynamische Arrays haben. Die Beispiele in diesem Artikel sind in PHP geschrieben, da PHP Java sehr ähnelt (mit dem Sie vielleicht vertraut sind), aber dynamische Arrays anstelle integrierter Sammlungsklassen und Schnittstellen verwendet.
Bei Verwendung eines Array als Wenn es sich um eine Liste (eine Sammlung von Werten in einer bestimmten Reihenfolge) handelt, sollte jeder Wert vom Typ z sein:
$goodList = [ 'a', 'b' ]; $badList = [ 'a', 1 ];
Ein allgemein akzeptierter Stil zum Kommentieren von Listentypen ist: @var array<TypeOfElement>
. Achten Sie darauf, den Typ des Index nicht hinzuzufügen (er ist immer int
).
PHP erstellt automatisch einen neuen Index für jedes Element in der Liste (0, 1, 2 usw.). Sie sollten sich jedoch nicht auf diese Indizes verlassen oder sie direkt verwenden. Die einzigen Eigenschaften von Listen, auf die sich Clients verlassen sollten, sind iterable und countable.
Sie können also gerne foreach
und count()
verwenden, aber verwenden Sie for
nicht, um die Elemente in der Liste zu durchlaufen:
// 好的循环: foreach ($list as $element) { } // 不好的循环 (公开每个元素的索引): foreach ($list as $index => $element) { } // 也是不好的循环 (不应该使用每个元素的索引): for ($i = 0; $i < count($list); $i++) { }
(In PHP for
Schleifen sind sogar möglich. Funktioniert nicht, da möglicherweise Indizes in der Liste fehlen und möglicherweise mehr Indizes vorhanden sind, als Elemente in der Liste vorhanden sind.
Möglicherweise möchten Sie Elemente nach Index (unset()
) aus einer Liste entfernen. Sie sollten jedoch array_filter()
verwenden, um eine neue Liste (ohne unerwünschte Elemente) zu erstellen, anstatt Elemente zu entfernen.
Ebenso sollten Sie sich nicht auf den Index eines Elements verlassen. Wenn Sie also array_filter()
verwenden, sollten Sie den Parameter flag
nicht verwenden, um Elemente basierend auf dem Index oder sogar Elemente basierend auf beiden Elementen zu filtern und Index.
// 好的过滤: array_filter( $list, function (string $element): bool { return strlen($element) > 2; } ); // 不好的过滤器(也使用索引来过滤元素) array_filter( $list, function (int $index): bool { return $index > 3; }, ARRAY_FILTER_USE_KEY ); // 不好的过滤器(同时使用索引和元素来过滤元素) array_filter( $list, function (string $element, int $index): bool { return $index > 3 || $element === 'Include'; }, ARRAY_FILTER_USE_BOTH );
, wenn der Schlüssel mit verknüpft ist, nicht mit dem Index (0,1,2 usw.). . Es steht Ihnen frei, Arrays als Karten zu verwenden (aus denen Werte über ihre eindeutigen Schlüssel abgerufen werden können).
Die erste Regel bei der Verwendung von Arrays als Karten ist, dass alle Schlüssel im Array vom gleichen Typ sein sollten (am häufigsten). diejenigen sind Schlüssel vom Typ string
).
$goodMap = [ 'foo' => 'bar', 'bar' => 'baz' ]; // 不好(使用不同类型的键) $badMap = [ 'foo' => 'bar', 1 => 'baz' ];
Das Gleiche gilt für die Werte in der Karte: Sie sollten vom gleichen Typ sein.
$goodMap = [ 'foo' => 'bar', 'bar' => 'baz' ]; // 不好(使用不同类型的值) $badMap = [ 'foo' => 'bar', 'bar' => 1 ];
Ein allgemein akzeptierter Annotationsstil für Zuordnungstypen ist: @var array<TypeOfKey, TypeOfValue>
.
Listen können aufgrund ihrer einfachen Natur sicher zwischen Objekten weitergegeben werden. Jeder Client kann es verwenden, um seine Elemente zu durchlaufen oder seine Elemente zu zählen, auch wenn die Liste leer ist. Zuordnungen sind schwieriger zu handhaben, da Clients möglicherweise auf Schlüssel angewiesen sind, die keine entsprechenden Werte haben. Das bedeutet, dass sie im Allgemeinen für das Objekt, das sie verwaltet, privat bleiben sollten. Clients dürfen nicht direkt auf die interne Zuordnung zugreifen, stattdessen werden Getter (und möglicherweise Setter) zum Abrufen von Werten bereitgestellt. Wenn für den angeforderten Schlüssel kein Wert vorhanden ist, wird eine Ausnahme ausgelöst. Wenn Sie die Karte und ihre Werte jedoch vollständig privat halten können, dann tun Sie dies.
// 公开一个列表是可以的 /** * @return array<User> */ public function allUsers(): array { // ... } // 公开地图可能很麻烦 /** * @return array<string, User> */ public function usersById(): array { // ... } // 相反,提供一种方法来根据其键检索值 /** * @throws UserNotFound */ public function userById(string $id): User { // ... }
für Zuordnungen mit mehreren Werttypen. Wenn Sie Werte verschiedener -Typen in einer Zuordnung speichern möchten, verwenden Sie bitte ein Objekt. Definieren Sie eine Klasse und fügen Sie ihr öffentliche typisierte Eigenschaften hinzu oder fügen Sie Konstruktoren und Getter hinzu. Beispiele für solche Objekte sind Konfigurationsobjekte oder Befehlsobjekte:
final class SillyRegisterUserCommand { public string $username; public string $plainTextPassword; public bool $wantsToReceiveSpam; public int $answerToIAmNotARobotQuestion; }
Manchmal muss eine Bibliothek oder ein Framework Arrays dynamischer verwenden Weg . In diesen Fällen ist es nicht möglich (und auch nicht wünschenswert), die vorherigen Regeln zu befolgen. Zum Beispiel Array-Daten, die in einer Datenbanktabelle gespeichert werden, oder Symfony-Formularkonfiguration.
自定义集合类是一种非常酷的方法,最后可以和Iterator
、ArrayAccess
和其朋友一起使用,但是我发现大多数生成的代码令人很困惑。第一次查看代码的人必须在 PHP 手册中查找详细信息,即使他们是有经验的开发人员。另外,你需要编写更多的代码,你必须维护这些代码(测试、调试等)。所以在大多数情况下,我发现一个简单的数组,加上一些适当的类型注释,就足够了。到底什么是需要将数组封装到自定义集合对象中的强信号?
如果使用相同数组的多个客户端执行相同的任务(例如过滤、映射、减少、计数),则可以通过引入自定义集合类来消除重复。将重复的逻辑移到集合类的一个方法上,允许任何客户端使用对集合的简单方法调用来执行相同的任务:
$names = [/* ... */]; // 在几个地方发现: $shortNames = array_filter( $names, function (string $element): bool { return strlen($element) < 5; } ); // 变成一个自定义集合类: use Assert\Assert; final class Names { /** * @var array<string> */ private array $names; public function __construct(array $names) { Assert::that()->allIsString($names); $this->names = $names; } public function shortNames(): self { return new self( array_filter( $this->names, function (string $element): bool { return strlen($element) < 5; } ) ); } } $names = new Names([/* ... */]); $shortNames = $names->shortNames();
在集合的转换上使用方法的好处就是获得了一个名称。这使你能够向看起来相当复杂的array_filter()
调用添加一个简短而有意义的标签。
如果一个客户端使用特定的数组并循环,从选定的元素中取出一段数据,并对该数据进行处理,那么该客户端就与所有涉及的类型紧密耦合: 数组本身、数组中元素的类型、它从所选元素中检索的值的类型、选择器方法的类型,等等。这种深度耦合的问题是,在不破坏依赖于它们的客户端的情况下,很难更改所涉及类型的任何内容。因此,在这种情况下,你也可以将数组包装在一个自定义 的集合类中,让它一次性给出正确的答案,在内部进行必要的计算,让客户端与集合更加松散地耦合。
$lines = []; $sum = 0; foreach ($lines as $line) { if ($line->isComment()) { continue; } $sum += $line->quantity(); } // Turned into a custom collection class: final class Lines { public function totalQuantity(): int { $sum = 0; foreach ($lines as $line) { if ($line->isComment()) { continue; } $sum += $line->quantity(); } return $sum; } }
让我们看看在使用自定义集合类时应用的一些规则。
对集合实例的现有引用在运行某种转换时不应受到影响。因此,任何执行转换的方法都应该返回类的一个新实例,就像我们在上面的例子中看到的那样:
final class Names { /** * @var array<string> */ private array $names; public function __construct(array $names) { Assert::that()->allIsString($names); $this->names = $names; } public function shortNames(): self { return new self( /* ... */ ); } }
当然,如果要映射内部数组,则可能要映射到另一种类型的集合或简单数组。与往常一样,请确保提供适当的返回类型。
你不必扩展泛型集合库类,也不必自己在每个自定义集合类上实现泛型筛选器、映射和缩减方法,只实现真正需要的。如果某个方法在某一时刻不被使用,那么就删除它。
如果你使用 PHP,不用实现所有的Iterator
接口的方法(并保持一个内部指针,等等),只是实现IteratorAggregate
接口,让它返回一个ArrayIterator
实例基于内部数组:
final class Names implements IteratorAggregate { /** * @var array<string> */ 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) { // ... }
为你的自定义集合类编写更多代码的好处是使客户端更容易地使用该集合(而不是仅仅使用一个数组)。如果客户端代码变得更容易理解,如果集合提供了有用的行为,那么维护自定义集合类的额外成本就是合理的。但是,因为使用动态数组非常容易(主要是因为你不必写出所涉及的类型),所以我不经常介绍自己的集合类。尽管如此,我知道有些人是它们的伟大支持者,所以我将确保继续寻找潜在的用例。
推荐教程:《PHP》
Das obige ist der detaillierte Inhalt vonArray-Spezifikationen und benutzerdefinierte Sammlungen in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!