简体   繁体   English

Java`final`类和嘲弄

[英]Java `final` class and mocking

I'm developing a programming game where players will have access to an abstract class and extend it to control the behaviour of a robot. 我正在开发一种编程游戏,玩家可以访问抽象类并扩展它以控制机器人的行为。 Because it is a programming game, I am trying to protect my game infrastructure so that players don't mess with the game instead of just the class that I give them; 因为它是一个编程游戏,我试图保护我的游戏基础设施,以便玩家不会混淆游戏,而不仅仅是我给他们的课程; for this I make most of my classes final but now I can't mock them in my unit tests (mockito + testNG). 为此,我将我的大部分课程定为final但现在我无法在单元测试中模拟它们(mockito + testNG)。

So I was wondering, how can I work around this? 所以我想知道,我该如何解决这个问题呢? is there a way to maybe leave the classes non-final for testing and then somehow automatically ' final -ize' them at a later stage of the build cycle (i'm using maven in case it's relevant for the answer). 有没有办法让这些类非最终用于测试,然后以某种方式在构建周期的后期自动“ final化”它们(我正在使用maven,以防它与答案相关)。 I don't want to add another external library or change my mocking library. 我不想添加另一个外部库或更改我的模拟库。
If it's not possible, then a second question: is making a class final really secure? 如果不可能,那么第二个问题让一个班级final真正安全吗? i saw some libraries that could remove the final classifier from bytecode, which makes me think that maybe then final is useless if it can be removed from an already compiled class 我看到一些库可以从字节码中删除final分类器,这让我觉得如果可以从已经编译的类中删除它,那么final也许是无用的

First, you can't prevent people from messing with your game by declaring things 'final'. 首先,你不能通过宣布“最终”来阻止人们搞乱你的游戏。 There are reflection tricks and whole code generation libraries that make it merely slightly inconvenient to get past this. 有反射技巧和整个代码生成库,只是稍微不方便通过它。

So, lose the finals, and then you can use jmock or whatever library you like to make mocks. 所以,输掉决赛,然后你可以使用jmock或任何你喜欢的库来制作模拟。

You can eventually try to apply automated refactoring tools, like Jackpot 3 or RefactoringNG . 您最终可以尝试应用自动重构工具,例如Jackpot 3RefactoringNG I never tested them but they are more than capable to do what you want if going the way of refactoring. 我从未对它们进行过测试,但如果采用重构的方式,它们就能胜任你想做的事情。

Another way would be to use Powermock which allows to mock finals and statics (even tough I'm not fond of mocking statics, as it suggests something is wrong in your design). 另一种方法是使用Powermock,允许模拟决赛和静电(甚至很难我不喜欢嘲弄静态,因为它表明你的设计有问题)。

You always can use delegation instead of inheritance. 您始终可以使用委派而不是继承。

public interface Foo {
    // ...
}

public final class FooImpl implements Foo {
    // ...
}

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

However, it is bad idea to have abstract classes in your API. 但是,在API中使用抽象类是个坏主意。 Interface will be better. 界面会更好。

IMO, classes are marked final to make their objects immutable. IMO,类被标记为final以使其对象不可变。 If you want to control the behavior of a class, you mark the methods in the class as private so that they cannot be overridden. 如果要控制类的行为,请将类中的方法标记为私有,以便不能覆盖它们。 If you leave them protected, then someone can extend your class, override these protected methods and pass an instance of the extended class to your APIs that expect your class object as paramter, thereby inducing the modified behavior. 如果你保护它们,那么有人可以扩展你的类,覆盖这些受保护的方法,并将扩展类的实例传递给你希望你的类对象作为参数的API,从而引发修改后的行为。 So, you would mark methods that you don't want to expose as private. 因此,您可以将不希望公开的方法标记为私有。

You leave only extension points in terms of protected/public methods which can be overridden for custom behavior, by the clients that use these classes. 根据使用这些类的客户端可以覆盖自定义行为的受保护/公共方法,只保留扩展点。

Making most classes final is good practice, since they are usually not designed for extension by subclassing. 使大多数类final是最好的做法,因为它们通常不是为子类化扩展而设计的。 All books on API design that I know of recommend doing so ("Effective Java", "Practical API Design", "API Design for C++"). 所有关于API设计的书籍我都知道建议这样做(“Effective Java”,“Practical API Design”,“API Design for C ++”)。 This is beneficial for several reasons, including the expression of the API designer's intent, the safe evolution of the API, and the prevention of dead code (for example, a good IDE will detect when a final method doesn't use one of its parameters, or never throws a checked exception listed in the throws clause - which would not be possible for a non-final method). 这有利于几个原因,包括API设计者的意图表达,API的安全演变以及防止死代码(例如,一个好的IDE将检测何时final方法不使用其中一个参数,或者永远不会抛出throws子句中列出的已检查异常 - 这对于非最终方法是不可能的。

As for mocking said classes, you just need to use a proper mocking tool such as JMockit (which I developed precisely because I wanted to write unit tests without sacrificing certain OO/API design practices). 至于模拟所说的类,你只需要使用一个适当的模拟工具,比如JMockit (我开发的正是因为我想编写单元测试而不牺牲某些OO / API设计实践)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM