如何避免PHP中Service类和Model类之间的隐式依赖关系

d.l*_*a38 4 php model-view-controller dependencies dependency-injection

如果我的网站设计遵循以下依赖流:

处理程序类<->服务类<->映射器类<->模型类

Handler 接收请求,Service 包含逻辑,Mapper 执行数据库查询并使用模型来处理数据,而模型代表数据库中的单个记录。

理想情况下,您应该明确定义相邻层的依赖关系。因此,如果我更改特定层,则只有两个直接相邻的层可能会受到影响。

然而,我意识到我可能使用了一些不好的做法。特别是,我让 Service 类调用 Mapper 类上的方法。Mapper 类将返回 Model 类的实例。但是,如果我随后从 Service 类中调用 Model 类的方法,则 Service 类和 Model 类之间现在存在直接且隐式的依赖关系。

我对如何解决这个问题有一些想法,我将在下面概述。但我觉得必须有更好的方法。所以我很好奇在避免这些隐式依赖情况同时避免不必要的处理方面的普遍共识是什么。

  1. 将模型的空实例注入服务类以使依赖关系显式化。但这看起来很笨拙,并且没有考虑多记录结果集。
  2. 将 Model 接口指定为该方法的 Mapper 接口中的返回类型。对我来说,这似乎是首选方法。但是,当您必须返回多记录结果集时,事情就会变得复杂。在我开始将结果用于实际应用程序逻辑之前,我宁愿不要不必要地遍历 Mapper 类中的整个结果集。至少,几乎每个查询的时间复杂度都是 O(2n)。所以我刚刚返回一个 \Iterable 对象,并在 Service 类中迭代该对象(因此 O(n) 是底数而不是 O(2n))。但这与模型类存在隐式依赖的情况相同。

我一直在尝试找出是否可以指定 \Iterable 将迭代的对象类型,就像使用数组一样(IE:int[]是整数数组)。我能想到的最好办法是创建一个特定于模型类的自定义迭代器类。这是消除这些隐式依赖性同时避免不必要的循环的首选方法吗?

非常感谢!

根据反馈进行编辑:

我没有任何失败的代码,并且这个问题并不意味着特定于任何实际代码。这个问题只是一个抽象问题。这就是为什么最初没有包含代码的原因。但根据反馈,我起草了以下内容,以帮助使目标情况更加清晰。

忽略 Handler 类,它不需要说明概念,我只是想用它来描述整体结构,以便更容易识别设计模式。

服务类别:

namespace App\Service;

class Service implements ServiceInterface 
{
    protected mapperInterface $mapper;

    public function __construct(mapperInterface $mapper){
        $this->mapper = $mapper;
    }

    public getAllRows() {
       $iterator = $this->mapper->getAllRows();
       while($iterator->valid()){
           $current = $iterator->current();
           // This line creates an implicit dependency on the Model class
           $name = $current->getName();
           $iterator->next();
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mapper类:命名空间App\Mapper;

class Mapper implements MapperInterface 
{
    protected modelInterface $modelPrototype;

    public function __construct(modelInterface $model){
        $this->modelPrototype = $model;
    }

    public getAllRows() : Iterator {
       // Execute SQL then hydrate into an iterator over multiple "Model" objects into a variable ($resultSet in this case)

       return $resultSet; // This is an Iterator instance of which each iteration contains a "Model" object.
    }
}
Run Code Online (Sandbox Code Playgroud)

模型类:

class Model implements ModelInterface 
{
    protected string $name;

    public function getName() : string {
        return $this->name;
    }

    public function setName(string $name) : void {
        $this->name = $name;
    }
}
Run Code Online (Sandbox Code Playgroud)

正是 Service 类中的这一行在 Service 类和 Model 类之间创建了隐式依赖关系(通过 Mapper 类绕过了传递依赖关系):

$name = $current->getName();
Run Code Online (Sandbox Code Playgroud)

有两种方法可以纠正此问题,要么明确依赖关系,要么首先消除依赖关系。我列出了关于如何实现这两个选项的想法。但我很好奇这种情况最常见的解决方案是什么。还有其他我没有想到的选择或方法来解决这个问题吗?或者是我提出的想法之一是普遍首选的方法。

Jim*_*nse 5

从技术上讲,我也没有看到依赖性。它向Service它的邻居索要Mapper一组Model,它提供了一个。使用MapperModel混合来自任何地方的数据。所以沟通方面一切都很好。

如果您要访问您的Service数据

$this->mapper->model->someModelMethod();
Run Code Online (Sandbox Code Playgroud)

代替

$this->mapper->getAllRows();
Run Code Online (Sandbox Code Playgroud)

只有这样你才会违反至少在得墨忒尔法则意义上的某些东西。

您可能认为存在问题的级别的依赖性可能是层内的某种紧密耦合,这对于此用例来说通常是可以的。

如果您想在层/模块/单元之间解耦它,请考虑引入另一种数据容器来传递。

例如,使用某种形式将Transfer Object模型中的数据映射/水化到它。该传输对象可用于在通信层(如控制器、服务等)上传递。您仅依赖于传输对象属性,而不依赖于模型本身。Model如果和 上都发生了变化,则只需调整您的映射器Transfer Object