When using jmock, you can mock a concrete class if you set the class imposteriser. I noticed that the class imposterizer is in the legacy package, so I really don't want to use it (especially because it super easy to extract an interface using the refactoring tools in my IDE). I don't like to have instance variables of concrete classes either.
Extracting the interface, however, I've noticed a pattern emerging in my code base. A lot of the time an interface has only one implementing concrete class. I prefer to use an interface wherever possible, but it seems really verbose to have all these extra files. Also, it's slightly tedious to update the interface and update the implementer every time I want to add a new method to the class.
Is this just the price you pay for proper abstraction or is there a better approach that I haven't thought of? Should all classes implement an interface even if the only thing in the interface is getters/setters?
If the interface is only getters and setters, that sounds like it's more to do with data than behaviour - and doesn't sound like the sort of thing I'd mock. I'm happy to use simple production code directly within tests for other classes. I only inject dependencies for classes providing services.
I feel your pain, but personally I do still write the interface, even if there's currently only one production implementation. Quite often I'll find I write a stub or fake implementation as well after mocking for a while. Fakes can end up being rather simpler to use (leading to clearer tests) unless you're really interested in testing the interaction between the caller and the service.
It also means that when someone wants to look at methods a particular dependency provides, they can see just the interface with no implementation involved.
Some general points I go by:
These points will hopefully help you to make simpler and more testable and maintainable designs. At least after a bit of experience trying to follow them.
If it's verbose and tedious it's bad style, no matter what the theoretical advantages are.
If it's easy to extract the interface using your tools why do it now? Why not do it when it actually has some real purpose. I see too much code that is architected with the future in mind instead of solving the problem at hand.
Of course all debate about these issues is just opinion. There are no facts to be found.
You mention "proper abstraction". Well, it's only proper if the interface makes sense for more than one class. Especially considering that you are extracting the interfaces from concrete classes, you're probably ending up with methods in the interface which shouldn't be there. That is of course, if it makes sense for the interface itself to exists in the first place.
The last thing I'm going to point out is that your interfaces don't seem particularly stable. You seem to be implying that adding methods to interfaces is commonplace for you. I'd question the abstraction of class when the contract of the class (i.e. its interface) is so volatile.
All in all, I'm not convinced that immediately providing an interface for everything constitutes proper abstraction. Rather, waiting for the class's public interface to stabilize first, and then considering a proper abstraction (i.e. not just hitting the "extract interface..."
button in IDE) is much better from a design perspective. Plus, a class might abstract into several unrelated interfaces, rather than a single interface.
If your interface is primarily properties, it's likely that all the implementing classes are related, maybe an abstract class would be better suited for your needs. Then you can avoid some of the boilerplate. The primary functions of the abstract class could still be abstracted further by an interface.
interface IRunner {
void run();
int doOtherThing();
}
abstract class Thing implements IRunner {
//this is class-specific
abstract void run();
//this is common to all 'Things'
int doOtherThing() { return 0; }
//as are these properties
public int getProp() {...}
public void setProp(int val) {...}
}
public class Goo extends Thing {
public void run() {
int i = getProp() + doOtherThing();
makeMagicGoo(i);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With