9

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).

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). 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? 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

Hilikus
  • 9,954
  • 14
  • 65
  • 118
  • 1
    I would suggest to use interfaces instead of classes. And use DI. – khmarbaise Oct 30 '12 at 13:03
  • It's impossible to prevent people from messing with your code once its running on their machines. This applies to all programs to some degree but to java programs even more so because java classes are so easy to decompile. No matter what you do, they can simply decompile your code, modify it, and recompile it, thus changing its behavior. – Wug Oct 30 '12 at 13:05
  • 1
    Including `final` was one of the worst design-decisions of the Java programming language. I dare anyone to show me one good example of a valid usage that actually adds value and I'll happily counter it with about 100 examples where it adds nothing but frustration, inflexibility and code-duplication. I'd be a happy developer if Oracle simply dropped `final` altogether. – pap Oct 30 '12 at 13:54
  • 1
    About your second question: using `final` is a matter of OO design and a way to help static analysis tools to help us write better code, it's not a way to make code "secure". Whether `final` or anything else about the bytecode can be modified at runtime is generally not relevant; a secure environment requires a sand-box, which Java supports through the `SecurityManager`, but this is usually only used in applets and in managed Java EE containers. – Rogério Oct 31 '12 at 10:46

5 Answers5

12

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.

bmargulies
  • 97,814
  • 39
  • 186
  • 310
  • 2
    Ok, that's what i thought then: `final` is more to show a designer's intent than to be a security enforcer (i.e "this class is not meant to be extented", more than "this class can't be extended"). That's what i thought originally but then I started reading about it and I kept finding 'security' as one of the reasons for it. I guess I read bad sources – Hilikus Oct 30 '12 at 17:12
  • 1
    If you can run the code in a JVM you control with a SecurityManager, then it has some security to it. But if you can do that, well, why do you need it? – bmargulies Oct 30 '12 at 20:47
  • 1
    As pointed out in other answers, you don't need to sacrifice design by losing finals, since mocking tools exist that don't have a problem with `final` methods or classes. – Rogério Oct 31 '12 at 10:52
2

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. Interface will be better.

2

You can eventually try to apply automated refactoring tools, like Jackpot 3 or RefactoringNG. 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).

bric3
  • 40,072
  • 9
  • 91
  • 111
0

IMO, classes are marked final to make their objects immutable. 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. 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.

Vikdor
  • 23,934
  • 10
  • 61
  • 84
0

Making most classes final is good practice, since they are usually not designed for extension by subclassing. All books on API design that I know of recommend doing so ("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).

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).

Rogério
  • 16,171
  • 2
  • 50
  • 63