【问题标题】:Is there ever any advantage to using static functions / variables versus a class / objects?使用静态函数/变量与类/对象相比有什么优势吗?
【发布时间】:2018-02-19 05:16:48
【问题描述】:

在 Robert C. Martin 的《清理代码》一书中,他清理了一个杂乱无章的类,并以这种方式以一个由静态变量和静态函数组成的文件结束。

public class PrimeGenerator{
    private static int[] primes; 
    private static ArrayList<Integer> multipleOfPrimes;

    public static int[] generate(int n){
        primes = new int[n];
        //call functions
        someFunctionThatModifiesPrimes()

        return primes;
    }

    private static void someFunctionThatModifiesPrimes(){
        //modify primes
        prime[x] = y;
    }
}

他写

请注意,它并不是要被实例化为对象。班上 只是一个有用的作用域,可以在其中声明和隐藏变量。

我的问题是,当我能做到这一点时,我为什么还要去做他所做的事情:

public class PrimeGenerator{
    private int[] primes; 
    private ArrayList<Integer> multipleOfPrimes;

    public PrimeGenerator(int n){
        primes = new int[n];
    }

    public int[] generate(int n){
        //call functions
        someFunctionThatModifiesPrimes()

        return primes;
    }

    private void someFunctionThatModifiesPrimes(){
        //modify primes
        prime[x] = y;
    }
}

他的代码:

a) 不是线程安全的,在素数已经生成(从多个线程调用)时调用“generate(int)”会导致失败。

b) 在完成运行后保留一个带有垃圾素数的全局变量,它只会在下次运行时被覆盖。

我能想到的唯一优点是它可能更快?即便如此,这也可以忽略不计。

需要创建对象的代码是线程安全的,不会保留垃圾数据并且没有静态状态。

【问题讨论】:

  • 是的,缺乏线程安全是 Martin 的 Clean Code 中的一个常见主题。可能是这本书年代久远的结果。人们过去不关心这些东西(即使在当时也是愚蠢的,并导致遗留代码库中的许多问题)。
  • @CodyGray 即使不考虑线程安全,我也很难理解这种代码风格与替代方案相比如何被认为是好的或干净的。考虑到相同的示例为 RowColumnPagePrinter 创建了一个类,他显然能够将事物包装到类/对象中,那么为什么这次他随机使用“静态”呢?不可能是随机的,但他似乎并没有在例子后面的文字中解释他的推理。
  • 我猜它只是让它更容易访问,这样你就不需要创建一个对象来生成和读取素数。它是一种将所有用于一个目的的变量和函数打包到一个类中的方法,同时避免了需要从对象调用所有内容的那种笨拙语法
  • @Mitchel0022 这似乎是一个滑坡,因为如果它是静态的,所有东西都可以被认为更容易访问,但在大多数情况下,它会导致糟糕的设计。制作一个对象并调用它似乎并没有违反书中的任何规则,而且他这样做是为了很多其他的事情,如果是这样的话,这些事情也可能是静态的。将简单的东西融入自己的课堂似乎是本书的重点,所以我觉得很奇怪他在这里不这样做。
  • 他的意图可能是primes 是唯一的数学值;一旦计算出来,就没有意义(而且很昂贵)重新计算它们。

标签: java code-formatting


【解决方案1】:

我认为在这里专注于一本书的叙述可能是一个问题。实际上,它是关于从整个社区获得共识,以找出最佳编码实践。找出在生产环境中更有效的方法。

做静态和非静态函数和变量是有原因的。但是,我会犹豫是否使用“优势”这个语言。

静态

如果您有与成员字段无关的纯逻辑(即成员字段永远不会被引用),那么将其设为静态可能会更好。基本上,在这种情况下你可能会做静态的原因是“关注点分离”,在某种程度上——如果你正在调试代码,或者试图阅读代码并且你知道它是一个静态函数,那么你就没有担心会员。

有时我将变量本身设为静态,并将它们设为类而不是对象的成员。事实上,像这样的情况可能是您的素数示例。素数不会从PrimeGenerator 的一个实现更改为另一个实现,那么为什么要计算两次呢?你不会的。在计算完这些值之后,您将它们以对象的所有实例都可以访问的方式存储起来,或者外部调用者可以静态访问。

我认为反对线程安全的呼吁是一个巨大的逃避。为什么它不能是线程安全的?您可以使用synchronized(PrimeGenerator.class)java.util.concurrent.*Collections.unmodifiable*(*) 的任意组合来使其成为线程安全的。

考虑到这一点,在全局/常量和范围限定为稀疏实例化的对象之间有一条细线。事实上,重新审视素数的例子,很容易看出它是如何被认为是一个常数(有无限的素数,但在计算上是有限的,这意味着我们可以得到完整的集合,因为我们定义它是完整的) .常量始终是静态的,只是因为没有理由为此浪费对象上的空间。所以真的,这个的核心是素数的例子实际上是那本书作者的一个非常糟糕的例子。那么,什么不是常数而是静态的东西是一个很好的例子?

立即想到的例子是java.util.logging.Logger,但我会跳过它,因为根据我针对质数给出的论点,试图争辩它不是一个常数可能会更难。相反,我会选择java.util.concurrent.atomic.AtomicBoolean,如果你能忍受我一会儿。考虑一个场景,其中类Foo 的多个实例正在执行某种工作,这些工作都严格基于一些AtomicBoolean,称为bar,因此他们需要在尝试执行某些操作之前检查bar 的状态工作。你可能会做类似观察者模式的事情,并通知Foo的所有实例化,当然,但是你会在通知上浪费cpu周期(不管最终是多少),同时,每个实例化都需要存储该值关闭,重复,与所有其他实例化相同。或者,您可以只使用public static final AtomicBoolean bar = new AtomicBoolean();,例如,每个实例化只需执行Foo.bar.compareAndSet(expected, newValue);

非静态

归根结底,这是一种偏好,但其中存在一些社区,以及一些效率问题。如果你真的不想使用静态,你绝对可以。您最终可能只是为了从函数中获取值而实例化一个类,然后将刚刚创建的 Object 转储到堆上,但您可以这样做。

我最近听到的一些反对静态的论点,我认为这些论点是一个更严肃的论点(但仍然没有任何分量)是代码不可测试。我会说,在生产环境中,JUnit 很重要。可测试的代码能及早发现错误,yada yada yada。那么这和静态有什么关系呢?好吧,开箱即用的框架,如Mockito(这是一个非常流行的框架)不支持开箱即用的模拟静态。所以很多非常薄弱的​​论点喜欢声称这意味着静态代码是不可测试的。

这根本不是真的,因为所有的模拟框架都在某种程度上支持静态模拟。 Mockito 本身甚至有一个 API,旨在与其他框架集成,例如 PowerMock。因此,虽然说 Mockito 不能模拟静态在技术上是准确的,但它有点误导,因为它专门设置了一种方法来以它可以的方式进行集成。

其中有一些关于模拟框架如何使用类以及如何与类加载器交互的小警告,但我觉得这有点超出了范围。 (而且我不敢说超出范围,因为它确实赋予了非静态更多的权重,并且看起来我在为静态争论,但我不是,但这里有很多要谈的 - 哪个涉及类加载器、模拟框架、Java 字节码等 - 这远远超出了这个问题的范围)

结论(?)

冗长的方法是,静力学没有错。当你有一个有状态的对象时不要使用它们,你会没事的。注意静态上下文中的线程安全(您也应该在非静态上下文中这样做,所以这不应该是一个新概念),并且不要破坏编码约定。我知道这听起来有点笼统,但实际上这就是它的全部内容。我发现吹捧静态不好的人往往对此没有真正的反对意见,而且通常是关于他们想无缘无故地强加给世界的个人意识形态。所以它真的归结为倾听反对它的论点,看看它们是否有分量。如果他们不这样做,那么这是您自己的个人判断。就你引用的那本书而言,我想说这个论点没有分量。

【讨论】:

    猜你喜欢
    • 2012-11-08
    • 2015-11-25
    • 2012-05-02
    • 2015-07-20
    • 2018-10-08
    • 2012-12-09
    • 2010-12-14
    • 1970-01-01
    • 2010-09-24
    相关资源
    最近更新 更多