这差没有可能是一个对于数组设计的格调指南,然而把它增加到工具设计格调指南觉得没有太对,由于没有是一切的面向工具言语都有静态数组。本文中的示例是用 PHP 编写的,由于 PHP 很像 Java(可能比拟相熟),然而应用的是静态数组而没有是内置的荟萃类以及接口。
应用数组作为列表
一切元素都应该具备相反的类型
当应用一个数组作为一个列表(一个具备特定程序的值的荟萃)时,每一个值应该是 z 类型:
$goodList = [ 'a', 'b' ]; $badList = [ 'a', 1 ];
一个被普遍承受的正文列表类型的格调是:@var array<TypeOfElement>
。 确保没有增加索引的类型(它老是int
)。
应该疏忽每一个元素的索引
PHP 将主动为列表中的每一个元素(0、一、2等)创立新索引。但是,你不该该依赖这些索引,也不该该间接应用它们。客户端应该依赖的列表的惟一属性是可迭代的以及可计数的。
因而,能够随便应用foreach
以及count()
,但没有要应用for
轮回遍历列表中的元素:
// 好的轮回: foreach ($list as $element) { } // 欠好的轮回 (地下每一个元素的索引): foreach ($list as $index => $element) { } // 也是欠好的轮回 (不该该应用每一个元素的索引): for ($i = 0; $i < count($list); $i++) { }
(正在 PHP 中,for
轮回乃至可能没有起作用,由于列表中可能短少索引,并且索引可能比列表中的元素数目还要多。)
应用过滤器而没有是删除了元素
你可能心愿经过索引从列表中删除了元素(unset()
),然而,你应该应用array_filter()
来创立一个新列表(不没有需求的元素),而没有是删除了元素。
一样,你不该该依赖于元素的索引,因而,正在应用array_filter()
时,不该该应用flag
参数去依据索引过滤元素,乃至依据元素以及索引过滤元素。
// 好的过滤: 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 );
应用数组作为映照
当键是相干的,而没有是索引(0,1,2,等等)。你能够随便应用数组作为映照(能够经过其惟一的键从此中检索值)。
一切的键应该是相反的类型
应用数组作为映照的第一个规定是,数组中的一切键都应该具备相反的类型(最多见的是string
类型的键)。
$goodMap = [ 'foo' => 'bar', 'bar' => 'baz' ]; // 欠好(应用没有同类型的键) $badMap = [ 'foo' => 'bar', 1 => 'baz' ];
一切的值都应该是相反的类型
映照中的值也是如斯:它们应该具备相反的类型。
$goodMap = [ 'foo' => 'bar', 'bar' => 'baz' ]; // 欠好(应用没有同类型的值) $badMap = [ 'foo' => 'bar', 'bar' => 1 ];
一种普遍承受的映照类型正文款式是: @var array<TypeOfKey, TypeOfValue>
。
映照应该放弃公有
列表能够平安地正在工具之间通报,由于它们具备简略的特色。任何客户端均可以应用它来轮回其元素,或计数其元素,即便列表是空的。映照则更难解决,由于客户端可能依赖于不对应值的键。这象征着正在普通状况下,它们应该对治理它们的工具放弃公有。没有容许客户端间接拜访外部映照,而是提供 getter (可能另有 setter )来检索值。假如申请的键没有存正在值,则抛出异样。然而,假如您能够放弃映照及其值齐全公有,那末就这样做。
// 地下一个列表是能够的 /** * @return array<User> */ public function allUsers(): array { // ... } // 地下舆图可能很费事 /** * @return array<string, User> */ public function usersById(): array { // ... } // 相同,提供一种办法来依据其键检索值 /** * @throws UserNotFound */ public function userById(string $id): User { // ... }
对具备多个值类型的映照应用工具
当你想要正在一个映照中存储没有同类型的值时,请应用一个工具。界说一个类,并向其增加公共的类型化属性,或增加结构函数以及 getter。像这样的工具的例子是设置装备摆设工具,或许饬令工具:
final class SillyRegisterUserCo妹妹and { public string $username; public string $plainTextPassword; public bool $wantsToReceiveSpam; public int $answerToIAmNotARobotQuestion; }
这些规定的破例
有时,库或框架需求以更静态的形式应用数组。正在这些状况下,不成能(也没有心愿)遵照后面的规定。例如数组数据,它将被存储正在一个数据库表中,或许Symfony 表单设置装备摆设。
自界说荟萃类
自界说荟萃类是一种十分酷的办法,最初能够以及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->isCo妹妹ent()) { 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->isCo妹妹ent()) { 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( /* ... */ ); } }
当然,假如要映照外部数组,则可能要映照到另外一品种型的荟萃或简略数组。与往常同样,请确保提供适当的前往类型。
只提供实际客户需求以及应用的行为
你不用扩大泛型荟萃库类,也不用本人正在每一个自界说荟萃类上完成泛型挑选器、映照以及缩减办法,只完成真正需求的。假如某个办法正在某一时辰没有被应用,那末就删除了它。
应用 IteratorAggregate 以及 ArrayIterator 来支持迭代
假如你应用 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》
以上就是PHP中数组标准以及自界说荟萃的具体内容,更多请存眷资源魔其它相干文章!
标签: php php开发教程 php开发资料 php开发自学
抱歉,评论功能暂时关闭!