依赖注入与静态方法


21

今天,我与另一位开发人员进行了有趣的讨论,内容涉及如何使用接受字符串并输出字符串的方法处理类。

想象一下以下内容,它完全是出于示例目的而组成的

public string GetStringPart(string input)
{ 
   //Some input validation which is removed for clarity

   if(input.Length > 5)
        return input.Substring(0,1);

   if(input.Substring(0,1) == "B")
        return input.Substring(0,3);

   return string.empty;
}

一个基于字符串输入而具有某种逻辑的函数将使用DI添加到项目中,并且具有一个DI容器。您是否会使用接口添加此新类并在需要时将其注入,还是将其设为静态类?各自的优缺点是什么?您为什么要(或不希望)使它与构造函数注入一起使用,而不是在需要时随处访问。


1
@Ewan用于没有抽象作用的辅助方法。示例:Apache FileUtils。
Walfrat

3
@Ewan:没有副作用的静态方法是最好的方法,因为它们易于理解且易于测试。
JacquesB

1
但它们无法对依赖于它们的事物进行单元测试
Ewan

3
@Ewan Eh,不是。Math.Max()是否因为静态而不好?如果测试静态方法并且该方法有效,则可以在其他方法上安全地使用它,而不会出现问题。如果静态测试失败,则其测试将捕获该测试。
T. Sar-恢复莫妮卡

1
如果“静态”保证没有副作用,我也许可以看到该论点。但事实并非如此。
伊万

Answers:


25

没有理由为什么需要注入。这只是一个函数,没有依赖性,因此只需调用它即可。如果您愿意,它甚至可以是静态的,因为它看起来是纯净的。可以毫不费力地针对此编写单元测试。如果在其他类中使用它,则仍可以编写单元测试。

不需要抽象出没有依赖关系的函数,这太过分了。

如果这变得更加复杂,则可能需要将接口传递给构造函数或方法。但是,除非有GetStringPart基于位置等的复杂逻辑,否则我不会走这条路。


2
这是正确的答案!糟糕的是,人们对货物狂热的回答已被接受。
JacquesB '17

3
函数没有依赖关系不是重点。问题在于其他事物取决于功能并与之紧密耦合
Ewan

2
如果函数在6个月内更改并且不是那么纯净,但已经在很多地方使用过,该怎么办。它不能作为静态扩展,也不能与使用类单元测试隔离。如果有很小的改变机会,我会注入它。就是说,我愿意听取反对意见,这是本文的重点。这种注入有什么负面影响以及静态的积极影响?
詹姆斯(James)

1
一般而言,在此网站上讨论依赖注入的人们并不需要依赖注入。因此,倾向于不必要的复杂性。
Frank Hileman '17

2
@James如果函数变得不那么纯净,则说明您做错了什么,并且该函数可能从一开始就没有很好地定义。计算正方形的面积不应突然需要数据库调用或为UI部署可视更新。您能想象一下一种“子字符串”方法突然变得不纯净的情况吗?
T. Sar-恢复莫妮卡

12

这就是为什么

class DOSClient {
    OrderParser orderParser;
    string orderCode;

    DOSClient(OrderParser orderParser, string ordercode) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
    }
    void DisplayOrderCode() {
        Console.Write( "Prefix: " + orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

class GUIClient {
    OrderParser orderParser;
    string orderCode;
    GUI gui;

    GUIClient(OrderParser orderParser, string ordercode, GUI gui) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
        this.gui = gui;
    }

    void DisplayOrderCode() {
        gui.Prefix( orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

 

class OrderParserUS : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 5)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "B")
            return input.Substring(0,3);

        return string.empty;
    }
}

class OrderParserEU : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 6)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "#")
            return input.Substring(0,3);

        return string.empty;
    }
}

如果您使用静态方法,则无法在不GetStringPart破坏旧行为或不使用条件逻辑对其进行污染的情况下更改的行为。确实,静电是变相的邪恶全局变量,但是它们使多态性失效的事实是我对它们的主要抱怨。静态方法不是OOP语言中的一流。通过为该方法提供一个对象,甚至没有状态的对象,我们可以使该方法可移植。它的行为可以像变量的值一样传递。

在这里,我已经想象过一个系统,在欧洲部署时与在美国部署时,其性能需要略有不同。而是强迫一个系统只包含另一个系统所需的代码,我们可以通过控制将顺序解析对象注入客户端的方式来更改行为。这使我们能够包含区域细节的分布。这也使添加OrderParserCanada变得容易,而无需接触现有的解析器。

如果那对您没有任何意义,那么这确实没有一个很好的论据。

顺便说一句,这GetStringPart是一个可怕的名字。


*尖括号代码风格*我猜你是一个试图编写C#代码的Java程序员吗?我猜想一个就知道。:)
Neil

这个问题不是特定于语言的,而是关于设计的。静态方法的问题是它们阻止了测试代码的隔离。与我交谈的开发人员认为我过分依赖DI,有时扩展方法/静态方法很重要。我认为可能在极少数情况下,但不是通用的。我认为正在进行的讨论是进一步讨论的好方法。我也同意您关于多态性的观点。
詹姆斯

测试是一个原因,但还有更多。我不想将此归咎于测试,因为这只会使人们批评测试。一个简单的事实是,如果您不按程序进行编程,那么程序程序员就不会喜欢它。
candied_orange

你不能简单地说static getStringPartEU()吗?仅当该类别中还有其他方法也需要特殊的EU处理并且必须将它们视为一个整体时,您的示例才有意义。
罗伯特·哈维

2
您肯定是在主张不必要的复杂性。一切都可以添加越来越多的代码。需要更改的内容应该易于修改,但是您不应尝试预测将来需要更改的内容。静态字段可以与全局变量相同,但是静态方法可以是纯净的,最佳的方法类型。
Frank Hileman '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.