如果实用程序 class 是反模式,那么 PHP 中的替代方案是什么?

If utility class is an anti pattern, what is the alternatives in PHP?

我正在尝试通过遵循最新的 OOP 设计模式来构建自己的 MVC 框架(当然是为了更好地学习它)。我想知道,放置可重复代码的最佳实践是什么(这些代码用于作为静态方法保留在实用程序 classes 中,这被认为不是一个好的模式)。

例如,我们想使用点分隔字符串遍历一个多维数组,我必须在几个 classes(它们是来自其他基 classes 的子classes 中使用这个算法=16=]es)。如何在不使用实用程序 class 且不多次重复相同代码的情况下做到这一点?

实用程序 class 没有任何问题,只是不要将所有不相关的实用程序函数集中到一个巨大的 class 中。根据它们的作用将它们分开(和命名空间)。例如,参见 Zend Filter or Symfony Filesystem

或者,如果需要此函数的 class 都有一个共同的父级,您可以将函数放在最顶层 class 或抽象。

或者,如果 class 没有共同的父代,您可以使用名为 extractArrayFromDottedString() 或类似方法的方法创建特征。

Laravel 通过定义独立的 "helper" 函数来做到这一点。 CakePHP 和 Yii 通过使用静态方法定义容器实用程序 classes(即 "Text" 或 "Xml")来实现。编程语言做类似的事情(即 PHP 的 implode()、Java 的 Math.round、C 的 strcpy、Python 的 sum(),等等)。几乎所有东西都使用独立函数或静态 class 方法。

最终,最好的选择是主观。这取决于想要如何组织事物。研究 PHP 中的常见设计模式,感受不同框架在实践中的感受。然后选择一种方法并保持一致。

Utility class is an anti pattern

真的没有。
在 OOP 中,使用实用程序 classes 设计整个应用程序很可能是一种反模式,但使用提供 routine/transverse 任务的 static 方法定义实用程序 classes 可能是有意义的。

不将实用程序方法与使用它们的现有 classes 的方法混合也可以提供实用程序 class 和消费者 classes 的更好的内聚性和可重用性。

当然,您可以使用实例方法定义 class。
这是有效的,更冗长但具有提高 class 可测试性的优点。如果您需要模拟调用或切换到实用方法的其他实现,则必须优先使用实例方法。

如果这些是实用函数,则在单独的命名空间中定义它们。类似于

<?php
namespace Utils;

function array_query($array, $query) {
   // code for traversing the array 
}

将它们放在一个或多个文件中就可以了。请记住将该文件包含在您应用程序的 boostrap 阶段。

底线:停止滥用静态 classes,我们现在有用于该垃圾的命名空间。

但是,并非所有您认为的 "utility functions" 都是真实的。如果您开始使用 OOP 代码,一些代码应该放在关联的 classes 中。例如 "email validation" 不应进入 "utility function",而应进入 class:

class EmailAddress {

    private $emailAddress;

    public function __construct($emailAddress) {
        $this->assertValidEmailAddress($emailAddress);
        $this->emailAddress = $emailAddress;
    }

    private function assertValidEmailAddress($emailAddress) {
       if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
           throw new DomainException("Not an email address");
       }
    }

    public function __toString() {
        return $this->emailAddress;
    }
}

并且这些重复的 "domain logic" 片段应该放在单独的实体中,然后您可以为其他 class 类型提示。然后你在某处使用它作为:

public function register(EmailAddress $email, SafePassword $password): User
{
   // your user registration logic
}

通过这种方式,您的各种服务可以执行活动,您可以使用 try-catch 改进验证。

P.S.
You might need to take a hard look at what you are doing. That dotted access utility is neat (I had it too like 10 years ago), but actually is is a "temporary fix" for a deeper problem: you shouldn't be dealing with so deep array, that you need to simplify accessing them.