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

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

处理程序接收请求,服务包含逻辑,映射器执行数据库查询并使用模型处理数据,而模型表示数据库中的单个记录.

理想情况下,您应该在相邻层上/从相邻层显式定义从属关系.因此,如果我更改特定的层,只有两个紧邻的层might会受到影响.

然而,我意识到我可能在使用一些糟糕的做法.具体地说,我让Service类调用Mapper类上的方法.Mapper类将返回Model类的一个实例.但是,如果我随后从Service类中调用Model类的方法,则现在Service类和Model类之间存在直接和隐式的依赖关系.

关于如何解决这个问题,我有几个 idea ,我将在下面概述.但我觉得肯定有更好的办法.所以我很好奇一般的共识是什么,避免这些隐式依赖情况,同时避免不必要的处理.

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

我一直在try 找出是否有可能指定Iterable将迭代的对象的类型,就像您对数组所做的那样(IE:int[]是一个整数数组).我能想到的最好办法就是创建一个特定于Model类的定制Iterator类.这是消除这些隐式依赖关系,同时避免不必要循环的首选方法吗?

非常感谢!

Edit based on feedback:

我没有任何失败的代码,这个问题并不是特定于任何真实的代码.这个问题完全是关于一个抽象的问题.这就是为什么最初没有包括代码的原因.但根据反馈,我起草了以下内容,以帮助更清楚地说明目标情况.

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

服务类别:

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();
       }
    }
}

映射器类: 命名空间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.
    }
}

Model类:

class Model implements ModelInterface 
{
    protected string $name;

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

    public function setName(string $name) : void {
        $this->name = $name;
    }
}

具体地说,正是Service类中的此行在Service类和Model类之间创建了隐式依赖关系(通过Mapper类绕过传递依赖关系):

$name = $current->getName();

有两种方法可以纠正这一点,要么显式地显示依赖关系,要么首先消除依赖关系.我已经将我的 idea 列在了how条上,以实现这两个 Select .但我很好奇,对这种情况最常见的解决方案是什么.有没有其他我没有想过的解决这个问题的 Select 或方法?或者是我提出的 idea 之一,通常是首选的方法.

推荐答案

从技术上讲,我也看不到依赖.Service个人向它的邻居Mapper索要Model个集合,它提供了一个.Mapper使用Model并对来自任何地方的数据进行加密.因此,在沟通方面,一切都很好.

如果您想要访问Service中的数据,请使用

$this->mapper->model->someModelMethod();

而不是

$this->mapper->getAllRows();

只有这样,你才会违反某些东西,至少在得墨特尔定律的意义上是这样.

对您可能认为有问题的级别的依赖可能是层内的某种紧密耦合,这对于这个用例通常是可以的.

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

例如,使用某种类型的Transfer Object,并将数据从您的模型映射/水化到它.此传输对象可用于传递,例如,在通信层,如控制器、服务等.您仅依赖于传输对象属性,而不依赖于模型本身.如果ModelTransfer Object都发生了变化,则只需调整您的 map 绘制程序

Php相关问答推荐

来自另一个表的SQL过滤器

Laravel 11 api资源PUT请求模型没有查询结果

相同的数组(一个来自func_get_args(),另一个显式构建)在用作参数时并不相同

更新WooCommerce高性能订单存储(HPOS)的管理员自定义订单元数据

CodeIgniter AJAX表格提交中的CSRF token 再生问题

模型中的Laravel自定义查询字段

以编程方式更改新订单、已取消订单和失败订单的邮箱WooCommerce通知

在WooCommerce中禁用特定日期之前的免费送货

获取要删除的图像的公共ID

WooCommerce常规价格增加额外的附加平价

如何不重新查询模型A-&>;相关模型B-&>模型

在WooCommerce获取所有具有全球产品属性品牌的简单产品

返回WooCommerce中的所有已启用变体

如何在微软图形API中使用用户S访问令牌或基于租户ID的访问令牌?

如何用Ubuntu 22.04在VSCode中启用XDebug

向WooCommerce管理订单总额添加自定义总额行

多个产品类别 Select 自定义 WooCommerce 插件设置页面

尽管有use指令,PHP 类在其他文件中仍处于可见状态

ACF 更新字段功能不更新 WordPress 中的任何数据

Laravel 8:会话不适用