【问题标题】:Java `final` class and mocking [duplicate]Java`final`类和模拟[重复]
【发布时间】:2012-10-19 19:51:11
【问题描述】:

我正在开发一个编程游戏,玩家可以在其中访问一个抽象类并对其进行扩展以控制机器人的行为。因为它是一个编程游戏,所以我试图保护我的游戏基础设施,这样玩家就不会弄乱游戏,而不仅仅是我给他们的课程;为此,我制作了大部分课程final,但现在我无法在单元测试中模拟它们(mockito + testNG)。

所以我想知道,我该如何解决这个问题?有没有办法让类非最终测试,然后在构建周期的后期以某种方式自动“final-ize”它们(我正在使用 maven,以防它与答案相关)。我不想添加另一个外部库或更改我的模拟库。
如果不可能,那么第二个问题:让一个类final 真的安全吗? 我看到一些库可以从字节码中删除final 分类器,这让我觉得可能是@如果 987654325@ 可以从已经编译的类中删除,则它是无用的

【问题讨论】:

  • 我建议使用接口而不是类。并使用 DI。
  • 一旦你的代码在他们的机器上运行,就不可能防止人们弄乱你的代码。这在某种程度上适用于所有程序,但更适用于 java 程序,因为 java 类很容易反编译。无论您做什么,他们都可以简单地反编译、修改和重新编译您的代码,从而改变其行为。
  • 包含final 是Java 编程语言最糟糕的设计决策之一。我敢于向我展示一个实际增加价值的有效用法的好例子,我会很高兴地用大约 100 个例子来反驳它,这些例子除了增加挫败感、不灵活和代码重复之外什么也没有。如果 Oracle 完全放弃 final,我会是一个快乐的开发者。
  • 关于你的第二个问题:使用final是OO设计的问题,也是一种帮助静态分析工具帮助我们编写更好代码的方法,而不是让代码“安全”的方法。 final 或有关字节码的任何其他内容是否可以在运行时修改通常无关紧要;一个安全的环境需要一个沙箱,Java 通过SecurityManager 支持它,但这通常只用于小程序和托管 Java EE 容器中。

标签: java unit-testing maven mocking mockito


【解决方案1】:

创建大多数类final 是一种很好的做法,因为它们通常不是为通过子类化扩展而设计的。我所知道的所有关于 API 设计的书籍都推荐这样做(“Effective Java”、“Practical API Design”、“API Design for C++”)。这有几个原因,包括 API 设计者意图的表达、API 的安全演进和死代码的预防(例如,一个好的 IDE 将检测到 final 方法何时不使用它的参数,或者从不抛出 throws 子句中列出的已检查异常 - 这对于非最终方法是不可能的)。

至于模拟上述类,您只需要使用适当的模拟工具,例如 JMockit(我开发它正是因为我想在不牺牲某些 OO/API 设计实践的情况下编写单元测试)。

【讨论】:

    【解决方案2】:

    您最终可以尝试应用自动重构工具,例如 Jackpot 3RefactoringNG。我从未测试过它们,但如果采用重构的方式,它们完全有能力做你想做的事。

    另一种方法是使用允许模拟决赛和静态的 Powermock(即使很难,我也不喜欢模拟静态,因为它表明您的设计中有问题)。

    【讨论】:

      【解决方案3】:

      您始终可以使用委托而不是继承。

      public interface Foo {
          // ...
      }
      
      public final class FooImpl implements Foo {
          // ...
      }
      
      public class MockFooImpl implements Foo {
          private FooImpl delegate;
          // ...
      }
      

      但是,在您的 API 中包含抽象类是个坏主意。界面会更好。

      【讨论】:

      • 由于继承不是我做的,而是模拟库做的,所以这不是一个选项
      【解决方案4】:

      IMO,类被标记为 final 以使其对象不可变。如果你想控制一个类的行为,你将类中的方法标记为私有的,这样它们就不能被覆盖。如果您让它们受到保护,那么有人可以扩展您的类,覆盖这些受保护的方法并将扩展类的实例传递给期望您的类对象作为参数的 API,从而引发修改的行为。因此,您可以将不想公开的方法标记为私有。

      您只留下保护/公共方法方面的扩展点,这些方法可以被使用这些类的客户端覆盖以实现自定义行为。

      【讨论】:

        【解决方案5】:

        首先,您不能通过将事情声明为“最终”来阻止人们弄乱您的游戏。有一些反射技巧和完整的代码生成库,只是稍微有点不方便克服这一点。

        所以,输掉决赛,然后你可以使用 jmock 或任何你喜欢的库来制作模拟。

        【讨论】:

        • 好吧,我当时就是这么想的:final 更多地表明了设计者的意图,而不是成为安全执行者(即“这个类不应该被扩展”,而不是“这个类不能延长”)。这就是我最初的想法,但后来我开始阅读它,我一直认为“安全”是它的原因之一。我想我读到了不好的资料
        • 如果您可以在使用 SecurityManager 控制的 JVM 中运行代码,那么它就具有一定的安全性。但如果你能做到,那你为什么需要它?
        • 正如在其他答案中所指出的,您不需要通过输掉决赛来牺牲设计,因为存在对final 方法或类没有问题的模拟工具。
        猜你喜欢
        • 2018-05-17
        • 1970-01-01
        • 2011-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-16
        • 1970-01-01
        • 2016-10-14
        相关资源
        最近更新 更多