微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

“静态域助手类”是环境上下文反模式吗?

如何解决“静态域助手类”是环境上下文反模式吗?

正如 Steven van Deursen 和 Mark Seemann 在 Manning 一书中所定义的,“依赖注入原则、实践和模式”(我推荐为必读),有一个特定的反对我来说很有趣的模式,那就是 Ambient Context Anti-Pattern。它也被描述为here。一般问题是,如果您将一些 volatile 依赖项放在静态类后面,则会将代码库的多个部分耦合到该 volatile 依赖项。

我的困惑始于 volatile 依赖的定义。 volatile 依赖的定义之一是这种依赖仍在开发中或未开发。所以我的问题是:如果这个依赖是一些定义明确的领域知识,比如一些特定领域的电子邮件验证器,放在域层,并且它仍然在一些代码更改下,如果我们把它视为反模式在静态类后面并在整个代码库中使用它? (我们谈论的是洋葱架构,其中领域位于中心,一切都指向领域层)

编辑 1

这是一个简化的域类示例:

public class EmailValidationService
{
   private static readonly Regex _emailValidationRegex = /*some regex*/;

   public static EmailValidationStatus IsValid(string email)
   {
     var isValid = _emailValidationRegex.Match(email).Length > 0; ;

     return isValid;
   }
}

这个验证器的消费者被放置在多个层中,例如用户输入验证、文件导入验证等。这里有两个简化的消费者:

public class RawDataimporter{

    private Employee ProcessEmployee(EmployeeRowData data)
    {
        if (string.IsNullOrEmpty(data.Identifier)) { return new InvalidEmployee(); }
    
        if (EmailValidationService.IsValid(data.Identifier) { return new Employee(data.Identifier); }
    
    }
}

public class EmailAttribute :
        ValidationAttribute
{
    public override bool IsValid(object value)
    {
        var status = EmailValidationService.IsValid((string)value);
        if (status.IsValid)
        {
            return true;
        }

        return false;
    }
}

解决方法

volatile 依赖的定义之一是这种依赖仍在开发中或未开发。

令我震惊的是,书中的这句话(我是合著者)并没有得到真正的阐述。书中是这样说的:

如果依赖项 [...] [t] 依赖项尚不存在或仍在开发中,则该依赖项应被视为易变的。

就是这样。没有提供更多信息。幸运的是,第一版包含更多信息。在那个版本中,前面的声明后面跟着以下句子:

这种依赖的明显症状是无法进行并行开发。

我不得不深入了解我们删除它的原因,但我们校对人员的反馈表明这第二句话有点令人困惑。但是我们没有改进那条线,我们似乎(意外地?)删除了它。在该要点中保留“并行开发”的概念会很有用,因为它参考了第 1.2.2 节,其中提到了以下关于并行开发的内容:

当软件开发项目增长到一定规模时,有必要让多个开发人员在同一个代码库上并行工作。在更大的范围内,甚至有必要将开发团队分成多个可管理规模的团队。每个团队通常被分配负责整个应用程序的一个领域。为了划分职责,每个团队开发一个或多个需要集成到最终应用程序中的模块。除非每个团队的领域真正独立,否则有些团队可能会依赖其他团队开发的功能。

让我再次尝试更正本书的遗漏,并详细说明为什么仍在开发中的依赖项可以被视为易变的。

当开发人员较多的应用程序时,未开发的功能隐含地变得不稳定,因为它的不存在或无法并行开发阻碍了开发。团队越大,或者开发一个软件的团队越多,这些未开发的部分对开发者性能的阻碍就越多。

大多数在由多个团队并行开发的代码库上工作的开发人员都经历过,你经常被其他人阻止,遇到更多的合并冲突,看到更多由错误合并的代码引起的错误,更多的代码和测试被破坏您所依赖的代码的更改。这些问题可能部分是由于对其他类的强依赖造成的(尽管不可否认,发生这种情况的原因还有很多)。通过将这些风险更高的部分隐藏在抽象之后,可以减轻这种风险。这意味着组织的结构和规模会对代码库的设计和结构产生影响。您应该选择最具成本效益的设计(从长远来看)。

从这个陈述中,您可以得出结论,某个东西是 Volatile 依赖项还是 Stable 依赖项并不总是一门硬科学。这与包含非确定性行为的依赖关系完全不同。它们总是易变的。

注意:我会看看我们是否可以将其中的一些信息放入本书的勘误表中。遗憾的是,无法在下一次印刷中更新文本,因为这会导致页面布局和编号发生级联(扫描)更改,这是我们的出版商所不允许的。

如果该依赖是一些定义明确的领域知识,例如某些特定领域的电子邮件验证器,放置在领域层中,并且仍在进行一些代码更改,如果我们将其放在后面,它是否被视为反模式静态类并在整个代码库中使用它?

你的电子邮件验证器是一个很好的例子,我会把它放在灰色类别中;它可以走任何一条路。它的行为是确定性的,因为:给定相同的输入,它总是会产生相同的输出。不过,您可能会问的另一个问题是,这里是否需要松耦合?如果您希望能够将一个电子邮件验证器替换为另一个,或者更一般地说,添加更多验证检查是否需要更改电子邮件验证器的使用者,就会发生这种情况?这将是一个很好的迹象,表明您又在谈论易失性依赖。

现在让我们回到您关于 Ambient Context 反模式的问题。假设你认为这个验证是一个易失性依赖;这仍然不一定使它成为环境上下文。环境上下文的特征之一是可以替换行为。例如,请看以下演示环境上下文的示例;这是本书清单 5.11 的简化版本:

public static class TimeProvider
{
    public static ITimeProvider Current { get; set; }
        = new DefaultTimeProvider();
}

静态 TimeProvider 类允许访问 ITimeProvider 依赖项。您希望能够替换时间提供者行为这一事实非常非常强烈地表明这是一种易失性依赖。

如果不能用另一种依赖替换(以一种或另一种方式)易失性依赖,就没有环境上下文。例如,当您允许替换基础 Regex 时,您的验证器将成为环境上下文,如下所示:

public class EmailValidationService
{
   // Ambient Context
   public static Regex EmailValidationRegex { get; set; } = /*some regex*/;

   public static EmailValidationStatus IsValid(string email)
   {
     var isValid = EmailValidationRegex.Match(email).Length > 0; ;

     return isValid;
   }
}

Regex 是一个具体的类——不是抽象——,但这并没有改变它仍然是环境上下文的一个例子的事实。

相反,如果您确定此静态类包含易失性行为,并且此类由消费者直接调用,则本书将其视为 Control Freak 反模式的实现:

每当您在组合根以外的任何地方依赖易失性依赖项时,都会出现 Control Freak 反模式。这违反了依赖倒置原则 [...]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?