我提交了我写给其他一些架构师的应用程序,以供代码审查。其中一个几乎立即给我回信说:“不要使用static。您不能使用static类和方法编写自动测试。要避免使用static。”

我检查并完全检查了1我班的/ 4被标记为static。当我不打算创建类的实例时,我会使用static,因为该类是在整个代码中使用的单个全局类。

他接着提到了涉及模拟,IOC / DI技术的内容,不能与静态代码一起使用。他说不幸的是,由于第三方第三方库是不可测试的,因此它们是静态的。

其他建筑师正确吗?

更新:这是一个示例:

APIManager-此类保留我正在调用的第三方API的字典以及下一个允许的时间。它强制执行许多第三方在其服务条款中规定的API使用限制。在拨打电话之前,我会在通过拨打Thread.Sleep(APIManager.GetWait("ProviderXYZ"));呼叫第三方服务的任何地方使用它。这里的所有内容都是线程安全的,并且与C#中的TPL一起使用时效果很好。

评论

静态很好静电场需要非常小心地处理

@Артём字段是类级别的变量;静态字段是按类型而不是按实例的类级变量

对于我来说,该异议太笼统了。在大多数情况下,您不会移动静态类,而是嘲笑参数。静态字段,它是需要模拟的聚合类,然后是。

显然,您的同事病情很重-急性全身性红斑性红斑狼疮。他们需要尽快接受治疗!

有一个提供答案的答案部分,我不明白为什么人们要试图给某人一个答案/建议发表评论……这违背了StackExchange的目的,肯定会要求提供更多信息。

#1 楼

这取决于静态类是否保持状态。就我个人而言,在静态类中将无状态函数组合在一起没有问题。

评论


正确。没有副作用的静态函数是所有函数中可测试性最高的函数。

–罗伯特·哈维(Robert Harvey)
2012年8月13日在21:58

+1,但带有安全条款:几乎每个抽象算法都是无状态的,但这并不意味着您应该将其实现为无状态的静态类或方法。以这种方式实现它会使使用它的所有代码几乎都无法链接到它。例如,这意味着如果您将排序算法实现为静态方法并在项目中使用它-您将无法暂时将排序替换为另一种算法,即出于测试目的和缩小问题范围。在许多情况下,这是一个巨大的障碍。除此之外,如果合理使用static完全可以。

–quetzalcoatl
2012年8月14日在1:36

简而言之:它们是可测试性最高的功能,但不一定使它们使其他代码更具可测试性!这就是建筑师可能的意思。

–quetzalcoatl
2012年8月14日在1:37

@tereško:如果您不想创建类的实例来调用该方法,则C#语言要求将静态方法作为静态类的一部分。也许您的意思是“实例”,而不是“阶级”。

–罗伯特·哈维(Robert Harvey)
2012年8月14日15:06

@quetzalcoatl:将委托(即不同的算法)传递给静态方法是完全合法的; Linq中的扩展方法都是静态方法。例如:msdn.microsoft.com/en-us/library/…

–罗伯特·哈维(Robert Harvey)
2012年8月14日15:09

#2 楼

他对此太笼统了。他是正确的,这阻碍了测试。但是,static类和方法有其位置,它实际上取决于上下文。没有代码示例,您将无法真正分辨。


当我不打算创建类的实例时,我会使用static,因为该类是整个代码中使用的单个全局类。


这可能是严重的代码异味。您将这些课程用于什么?要保存数据?要访问数据库?那他是正确的。在这种情况下,您应该考虑依赖项注入,因为这样的静态类实际上是隐式的单例。

如果您将它们用于不更改状态而仅操作的扩展方法或助手在您提供的参数上,通常没问题。

评论


+1表示该声明是针对常规的,并提到了扩展方法,据我所知,该方法也可以进行测试,并且依旧可以模拟依赖项。

–不
2012年8月13日在22:02

静态作为单个类实例作为隐式单例,这会变得混乱...。

–托尼·霍普金森
2012年8月13日在22:16

“要访问数据库?”为什么不 ?如果表适配器不是静态的,则创建一个静态类@强类型数据集,它没有任何问题....除非您通过引用或示例证明自己的观点?

–数学
15-10-16在13:24



#3 楼


我检查了一下,并将全部1/4的班级标记为“静态”。当我不打算创建类的实例时,我会使用
静态方法,因为
该类是整个代码中使用的单个全局类。


要做的是尝试对代码进行单元测试。尝试设计可重复,独立,简单的测试,一次只测试一种方法。尝试以不同的随机顺序运行测试。您可以获得稳定的“绿色”建筑吗?

如果是,那是保护您的代码的正确点。但是,如果遇到困难,那么也许应该使用基于实例的方法。

评论


这也是让我印象深刻的一点。可以使用一些静态和无状态的“帮助程序”样式类,但是如果您将全部类的25%当作静态类,则您的努力不太可能仅限于这种情况。

–马修·沙利(Matthew Scharley)
2012年8月14日,0:57

@MatthewScharley-他可能有4个课程,其中1个是Static :)

– Alexus
2015年6月23日20:25在

请注意,如果您使用这样的固定器(例如单例),那么以后创建更多实例可能会很困难。像制作一个处理文档的应用程序,以后要管理多个文档时就会遇到问题。

– Michel Keijzers
16年8月5日,12:34

#4 楼

已经发布的答案涵盖了许多非常好的要点,但是似乎缺少了一个要点:

从不对静态字段进行垃圾收集。

您的应用程序存在很多内存限制,当人们尝试实现缓存时,这种模式可能很常见。

静态功能并不差劲,但其他人都已经足够介绍了已经详细。

评论


真的吗?如果我有具有5个项目的Cache.LanguageList(LanguageList是静态字段),然后执行Cache.LanguageList = new List (),我的前5个项目将不会被垃圾回收?您知道如何从内存中删除前5个项目吗?

–山姆
20 Jun 12'在8:57



这是一个非常古老的答案,您是正确的-他们将被垃圾收集。更好的说法是字段永远不会超出范围,因此管理内存使用情况是一个非常手动的过程。

–riwalk
20年6月12日在14:51

谢谢您的澄清...现在,我可以放松一下...哈哈...!

–山姆
20年6月13日在10:39

#5 楼

从IoC / DI获得的优点之一是,类之间的大多数交互都是在接口之间协商的。这使单元测试变得容易,因为可以自动或半自动模拟接口,因此可以测试每个零件的输入和输出。此外,除了可测试性之外,在所有内容之间放置接口可以使您拥有尽可能模块化的代码-您可以轻松地替换一个实现,而不必担心担心搞砸了依赖项。

因为C#没有没有元类,一个类无法使用静态功能实现接口,因此类的静态功能最终会花费很多精力来实现纯IoC / DI对象模型。也就是说,您无法创建模拟程序来测试它们,而必须创建实际的依赖项。

如果您的公司/项目投入大量资金进行IoC,那么这当然是合理的关注,而痛苦是为了所有人的利益。但是,从体系结构的角度来看,我个人不认为应该遵循任何模型。使用静态方法可以实现某些事情,例如单例设计策略。我本人不太喜欢静态类,因为我认为它们倾向于开始失去OO的优点,但是有时候,库类可能比引擎更自然地表达某种东西。


Commenter让我想起了扩展方法,这些方法当然必须在C#的静态类中。这是合法的,并且是一个示例,说明纯IoC如何无法在C#中很好地工作,至少在您尝试利用该语言的广度的情况下如此。

评论


如果出于正确的原因将类设为静态并正确实现(例如扩展方法),则它们仍然非常可测试,因为它们没有外部依赖性,并且是自包含的,因此不需要模拟。接口需求不是由设计需求驱动的,而不是为了保持纯IoC / DI项目而最好地满足业务需求吗?

–不
2012年8月13日在22:12

您和我在总体上都同意,您应该为正确的工作选择正确的工具,而不受哲学的束缚。但是,如果在您的纯IoC / DI项目中建立了良好的文化,那么与将传统的自上而下的解决方案转换为IoC / DI一样,这会带来很大的伤害。工程需要考虑过渡成本。顺便说一句,我认为扩展方法并不能帮助您实现目标。我认为使用静态类的IoCish行为的更好解决方案是在编译时使用C预处理器指令在多个类之间进行选择

– jwrush
2012年8月13日23:43

呃。我很笨您的意思是用于HOLDING扩展方法的静态类,当然,这是您必须使用的方法。是的,当然,您需要一个静态类用于此目的。这让我无所适从:这表明C#不适用于IoC。 :)

– jwrush
2012年8月13日23:46

是的,没错,如果现有项目中存在一种现有文化,那将比帮助改变事情的进行方式更具破坏性。

–不
2012年8月13日23:58

#6 楼

静态方法很好用,并且在编程中应有的地位。听起来您的架构师拥有一个不完全支持静态方法的测试框架。如果您的代码是更大项目的一部分,那么满足架构师指南很重要。但是,在适当的时候,不要让该项目阻止您使用静态方法。

#7 楼

静态类有时会被滥用,应该是:


单例(其中非静态类具有静态成员和公共静态Instance变量,只能检索/创建一次。 />另一个类中的方法(状态很重要)。如果您有一个成员,该成员不使用该类中的数据,则该成员可能不属于该类。

它也可能是所谓的“实用程序”函数,是实用程序类的一部分实用程序类是仅包含静态方法并用作辅助函数而没有任何上下文的类。

评论


@topomorto因为该类不应由其构造函数实例化,否则会导致多个对象/实例,而是由一种特殊方法(通常为instance())<实例化,该方法始终返回同一实例,该实例在首次调用instance()之后实例化。

– Michel Keijzers
16年8月5日,12:32

@topomorto我刚刚检查了一下,您完全正确,只有一些成员是静态的(实例变量和实例函数)。我将相应地更改答案;感谢您的提及。

– Michel Keijzers
16年8月5日在21:51

#8 楼

我一直在使用静态属性来处理类的所有实例所共有的东西。而且我一直在使用静态方法来获取类对象组。我绝不是专家,但是到目前为止,它一直对我有用。

PHP示例:

class vendorData {
  private static $data_table = 'vendor_data'; // static property
  private $id; // instance property
  private $name; // instance property

  public function __construct($vendor_id) {
    if(!self::validId($vendor_id) ) { // static method
      return false; // id doesn't exist
    }
    $this->id = $vendor_id; // id has been validated
    $this->getProperties(); // object method
  }

  private static function validId($vendor_id) {
    // send db query to find if the id exists
    // return true/false;
  }

  private function getProperties() { // object method
    $sql = "SELECT * FROM `{self::$data_table}` // using static property
        WHERE `id` = {$this->id}"; // using object instance property
    // get resultset
    foreach($result as $property => $value) {
      $this->$property = $value; // object instance properties all set
    }
  }

  // and here
  public static function getBy($property,$value) { // static method to return object array
    $sql = "SELECT `id` FROM `{self::$data_table}` // using static property
      WHERE `$property` = '$value'";
    // send query, get $ids array
    $obj_array = array();
    foreach($ids as $id) {
      // create array of current class objects using special static keyword
      // meaning of static here is different than in property/method declarations
      $obj_array[$id] = new static($id);
    }
    return $obj_array;
  }
}


#9 楼

我会说他们有原则,但是声明(不要使用静态)可能是错误的。您的代码覆盖率是多少?如果数量很高,并且您对应用程序的单元测试感到满意,那么您就可以了。如果没有,那么我建议复查代码。您可能会发现静态类是否是原因,这取决于很多事情。

#10 楼

具有静态方法的类滥用OOP的原理。它不是面向对象编程,而是面向类的COP。

他们很少具有清晰的“个性”(在这里我用我最喜欢的人类比喻)或身份。所以他们不知道自己是谁。仅此一点就足以摆脱OOP中的这一概念,但是还有更多的东西。由于此类不知道它们是谁,所以它们往往从大到大。

第三类使您的代码绝对不可维护。静态方法的使用引入了隐藏的依赖关系。他们到处弹出,您永远都不知道课堂上正在发生什么。您不能仅仅通过查看构造函数签名来确保这一点。他们似乎是看不见的。添加另一个很容易!您不必修改任何方法的签名。您要做的就是调用静态方法。然后是一个丑陋的代码...

好吧,您可能已经猜到了。紧密耦合经常导致低内聚。如果有很多客户在使用某个类,那么耦合会越来越紧密。使用已经编写好的只需要很小的修改的类或方法就会引起很大的诱惑。只有一个方法标志参数。

用什么代替静态方法?好吧,我的建议是OOP。为什么不尝试一下?