热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PHP中数组规范和自定义集合

这差不多是一个关于数组设计的风格指南,但是把它添加到对象设计风格指南感觉不太对,因为不是所有的面向对象语言都有动态数组。

使用数组作为列表

所有元素都应该具有相同的类型

当使用一个数组作为一个列表(一个具有特定顺序的值的集合)时,每个值应该是 z 类型:

$goodList = [
    'a',
    'b'
];

$badList = [
    'a',
    1
];

一个被普遍接受的注释列表类型的风格是:@var array。 确保不添加索引的类型(它总是int)。

应该忽略每个元素的索引

PHP 将自动为列表中的每个元素(0、1、2等)创建新索引。然而,你不应该依赖这些索引,也不应该直接使用它们。客户端应该依赖的列表的唯一属性是可迭代的可计数的

因此,可以随意使用foreachcount(),但不要使用for循环遍历列表中的元素:

// 好的循环:
foreach ($list as $element) {
}

// 不好的循环 (公开每个元素的索引):
foreach ($list as $index => $element) {
}

// 也是不好的循环 (不应该使用每个元素的索引):
for ($i = 0; $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

映射应该保持私有

列表可以安全地在对象之间传递,因为它们具有简单的特征。任何客户端都可以使用它来循环其元素,或计数其元素,即使列表是空的。映射则更难处理,因为客户端可能依赖于没有对应值的键。这意味着在一般情况下,它们应该对管理它们的对象保持私有。不允许客户端直接访问内部映射,而是提供 getter (可能还有 setter )来检索值。如果请求的键不存在值,则抛出异常。但是,如果您可以保持映射及其值完全私有,那么就这样做。

// 公开一个列表是可以的

/**
 * @return array
 */
public function allUsers(): array
{
    // ...
}

// 公开地图可能很麻烦

/**
 * @return array
 */
public function usersById(): array
{ 
    // ...
}

// 相反,提供一种方法来根据其键检索值

/**
 * @throws UserNotFound
 */ 
public function userById(string $id): User
{ 
    // ...
}

对具有多个值类型的映射使用对象

当你想要在一个映射中存储不同类型的值时,请使用一个对象。定义一个类,并向其添加公共的类型化属性,或添加构造函数和 getter。像这样的对象的例子是配置对象,或者命令对象:

final class SillyRegisterUserCommand
{
    public string $username;
    public string $plainTextPassword;
    public bool $wantsToReceiveSpam;
    public int $answerToIAmNotARobotQuestion;
}

这些规则的例外

有时,库或框架需要以更动态的方式使用数组。在这些情况下,不可能(也不希望)遵循前面的规则。例如数组数据,它将被存储在一个数据库表中,或者Symfony 表单配置。

自定义集合类

自定义集合类是一种非常酷的方法,最后可以和IteratorArrayAccess和其朋友一起使用,但是我发现大多数生成的代码令人很困惑。第一次查看代码的人必须在 PHP 手册中查找详细信息,即使他们是有经验的开发人员。另外,你需要编写更多的代码,你必须维护这些代码(测试、调试等)。所以在大多数情况下,我发现一个简单的数组,加上一些适当的类型注释,就足够了。到底什么是需要将数组封装到自定义集合对象中的强信号?

  • 如果你发现与那个数组相关的逻辑被复制了。
  • 如果你发现客户端必须处理太多关于数组内部内容的细节。

使用自定义集合类来防止重复逻辑

如果使用相同数组的多个客户端执行相同的任务(例如过滤、映射、减少、计数),则可以通过引入自定义集合类来消除重复。将重复的逻辑移到集合类的一个方法上,允许任何客户端使用对集合的简单方法调用来执行相同的任务:

$names = [/* ... */];

// 在几个地方发现:
$shortNames = array_filter(
    $names, 
    function (string $element): bool { 
        return strlen($element) <5; 
    }
);

// 变成一个自定义集合类:
use Assert\Assert;

final class Names
{
    /**
     * @var array
     */
    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
     */
    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
     */
    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社区 其它相关文章!


推荐阅读
  • 提交后Activity4新 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • MACElasticsearch安装步骤及验证方法
    本文介绍了MACElasticsearch的安装步骤,包括下载ZIP文件、解压到安装目录、启动服务,并提供了验证启动是否成功的方法。同时,还介绍了安装elasticsearch-head插件的方法,以便于进行查询操作。 ... [详细]
  • 将熊猫数据框中的浮点数转换为整数原文:https://www. ... [详细]
  • 动态分页实现
    Code分页存储过程CREATEprocedurePagersqlstrnvarchar(4000),--查询字符串currentpageint,--第N页pagesizeint- ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 云开发与
    大家好,今天我来为大家分享一下,Linux命令查询小程序中的WePY云开发实践。WhyWePY首先,先分享一下为什么要选择WePY?在项目开始进行选型的时候,我可选的底层框架有We ... [详细]
  • C#datatable序列化后整数带有小数点或者小数点变成整数原来datatable的列有个datatype属性,可以指定为int类型或者decimal类型的,如果指定int类型, ... [详细]
author-avatar
眭嘀佶毅
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有