【问题标题】:Legitimate uses for static initializer?静态初始化器的合法用途?
【发布时间】:2015-04-06 17:37:35
【问题描述】:

我记得几年前我使用静态初始化程序来调用类级别的设置操作。我记得它有非常奇怪的行为,我只是决定避开它们。也许是因为我搞乱了上下顺序或者是新手。但我遇到了重新审视它们的需要,我想确保没有更好的方法可以简洁。

我知道这并不流行,但我经常有数据驱动的类来维护从数据库导入的实例的静态列表。

public class StratBand { 
      private static volatile ImmutableList<StratBand> stratBands = importFromDb();

      private final int minRange;
      private final int maxRange;

      private static ImmutableList<StratBand> importFromDb() { 
            //construct list from database here
      }
      //constructors, methods, etc
}

当我有几十个像这样的表驱动类时,这种模式非常简洁(是的,我知道它将类与一个数据/实例源紧密耦合)。

但是,当我发现 Google Guava 的优点时,我想使用 EventBus 在某个事件发布时更新静态列表。我将创建一个静态最终布尔变量,只是为了调用一个初始化注册的静态方法。

public class StratBand { 
      private static volatile ImmutableList<StratBand> stratBands = importFromDb();
      private static final boolean subscribed = subscribe();

      private final int minRange;
      private final int maxRange;

      private static ImmutableList<StratBand> importFromDb() { 
            //construct list from database here
      }
      //constructors, methods, etc

      private static boolean subscribe() {
            MyEventBus.get().register(new Object() { 
                @Subscribe
                public void refresh(ParameterRefreshEvent e) { 
                    stratBands = importFromDb();
                }
            });
        return true;
      }
}

这很快就变得很烦人,因为编译器会对从未使用过的订阅变量发出警告。此外,它只是增加了混乱。所以我想知道使用静态初始化器是否符合规定,如果我不将它解耦成两个或更多类,真的没有更好的方法。想法?

 public class StratBand { 
          private static volatile ImmutableList<StratBand> stratBands = importFromDb();

          static { 
           MyEventBus.get().register(new Object() { 
                    @Subscribe
                    public void refresh(ParameterRefreshEvent e) { 
                        stratBands = importFromDb();
                    }
                });
          }

          private final int minRange;
          private final int maxRange;

          private static ImmutableList<StratBand> importFromDb() { 
                //construct list from database here
          }
          //constructors, methods, etc


    }

【问题讨论】:

  • 它“不时尚”正是因为,正如您所发现的,它确实会导致“非常奇怪的行为”。通常最好不要让这些东西是静态的,而是将它们显式地传递到需要它们的地方(或通过依赖注入隐式地传递它们)。静态初始化器在创建单例或进行纯计算时很好,但它们通常不应该与文件系统、数据库或任何外部的东西交互,真的。
  • 我在等别人这么说。我得到了我欣赏的 DI 范式。但在我们准备好扩展到一个 DI 驱动的框架之前,我有点希望能够将我们所拥有的框架保持更长时间。
  • 你不需要在这里使用 DI,但如果你能让它工作,我会感到惊讶,它仍然非常脆弱,难以测试,坦率地说它会付出更多的努力,而不是正确地去做。
  • 在那一点上我可能会避免使用静态初始化器。

标签: java guava static-initialization event-bus


【解决方案1】:

所以我想知道使用静态初始化器是否符合规定

有趣的是

private static final boolean subscribed = subscribe();

private static final boolean subscribed;
static {
    subscribed = subscribe();
}

编译成完全相同的字节码。所以使用不必要的静态变量更糟糕。


但在我们准备好扩展到 DI 驱动的框架之前,

发现Guice。不要称它为框架(尽管它是)。它易于使用,让您摆脱static

或者手动操作。通过删除所有静态修饰符来重写您的类,并将其传递到您需要的任何地方。有时它相当冗长,但显式声明依赖项允许您单独测试类。

就是这样,你无法在不访问数据库的情况下测试StratBand,无论被测方法多么微不足道。问题是每个StratBand 实例与所有StratBands 的列表的耦合。

此外,您无法测试依赖于 stratBands 内容的行为,因为它总是从数据库中加载(当然,您可以相应地填充您的数据库,但这是一个很大的痛苦)。

首先,我会创建StratBandManager(或StratBands 或您喜欢的任何名称)并将所有静态功能移至其中。为了简化过渡,我会创建一个 temporary 类,其中包含像

这样的静态助手
private static StratBandManager stratBandManager = new StratBandManager();
public static ImmutableList<StratBand> stratBands() {
   return stratBandManager.stratBands();
}

然后全部弃用并用 DI 替换它(使用 Guice 或手动执行)。


I findGuice 即使对小型项目也很有用。开销很小,因为通常没有或几乎没有任何配置。

【讨论】:

  • 是的,我已经花了很多时间研究 Guice,虽然我还没有应用它并且想应用它。鉴于我们当前的业务环境和我们的功能,我们采取了极简主义的方法……我所说的极简主义是指如果简化了事情,就可以合并和减少类的数量。我肯定需要考虑您的过渡建议,因为社区似乎非常坚持解耦和使用 DI。
  • @ThomasN。我不会减少课程的数量。您可能希望为“管理器”使用嵌套类以保持文件数量较少,但其他类基本上是免费的(除了在 Android 上可能很重要的内存成本)。
  • 是的,我一直喜欢嵌套类,因为它将所有内容都包含在一个地方。我确实意识到,当超过某个复杂性阈值时,它可能会导致应变可维护性。我想我将稍微改进我的方法,并开始更多地利用包私有范式,以及接近 DI 友好的设计。
猜你喜欢
  • 1970-01-01
  • 2012-03-30
  • 2014-10-06
  • 1970-01-01
  • 2010-12-11
  • 2016-06-28
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多