5 ms·
> have to first find the single implementing class This _can_ be a problem with Java, admittedly. Sometimes you've got a single meaningful implementation, but
by alanfranz 6y ago
> have to first find the single implementing class
This _can_ be a problem with Java, admittedly. Sometimes you've got a single meaningful implementation, but you need an interface either for using it with services that implement such interface at runtime to create proxies (e.g. Spring, Hibernate, others) that augment its functionality, or to properly mock a dependency. BUT: the IDE should be your friend. Almost every Java IDE supports "go to the implementation".
And, what is the alternative? Fewer interfaces sometimes lead to even worse patterns (for testing: monkeypatching and concrete class subclassing). I prefer the clarity of declaring an interface wherever an interface makes sense.
Using a lot of interfaces and defining APIs (which are, mostly, collections of related interfaces) makes the Java ecosystem highly pluggable. I can mix-and-match a lot of different libraries and/or implementations, as long as there's an interface they can work together in a rather good fashion.
Consider Python: there're very few (and sometimes poorly defined) interfaces and API; so, quite often you need to pick a library/framework which implements everything and works poorly with other implementations.
- arkh 6y ago> I can mix-and-match a lot of different libraries and/or implementations, as long as there's an interface they can work together in a rather good fashion. But are you writing a library? If not, your product does not have to be plugable. Also if you end with multiple interfaces with only one implementation you should start with classes and extract interfaces when needed. And I think the Java abstraction root problem comes from the way of doing "unit-testing" at the class level. Your users don't care about your application architecture: they want functionalities. Method and class level unit testing is just fossilizing your code and giving you some kind of feel-good number. You should be able to drop all your code, start anew with a new stack and have your tests still usable. Refactoring should never mean having to rewrite tests.
- corty 6y agoImho, organizing code in a braindead way (like single-implementation-interfaces) just to be able to test it is one of the main problems of bad code and bad languages. You make your code harder to understand, more convoluted and less refactorable. The far better approach is to seek out side-effects and state and organize your code like you would in functional languages like Haskell. Then testing is just adding a) testing all the stateless, functional and stateful stuff by pushing through prepared test objects and b) testing the side-effectful stuff through mocked side-effects or a test-mode (which you need anyways for integration tests). Cleaner, faster, easier to understand, easier to reason about. Only problem is that Java is relatively bad to use in a fuctional style.
- alanfranz 6y ago> But are you writing a library? If not, your product does not have to be plugable. I'm not writing a libraries, but other people are. In Java I can pick my IoC container, my ORM, my web stack, as I wish. I can hook-in web-related functionalities through the servlet API filters. If I pick Django in Python I get a builtin ORM and web stack. Everything is within Django, for Django; extensions are Django-specific.
- alanfranz 6y ago> You should be able to drop all your code start anew with a new stack and have your tests still usable In order for that to work, you'll need your interfaces to be stable. It's impossible otherwise :-)
- arkh 6y agoAnd what kind of interface do your app have? I'd guess a graphical one or some network endpoints. You should not test code. You should test functionalities. You should be able to rewrite all your app in a new language and still be confident your tests will tell you if something broke. "Unit testing" at the class level won't do that.
- alanfranz 6y ago> You should not test code. You should test functionalities. You're talking about integration tests and/or end-to-end tests. I write those. Usually at the façade level, not at the network endpoint (which usually means the controller level, a shallow layer that often includes some ACL and serialization) but it's just a matter of taste. > You should be able to rewrite all your app in a new language and still be confident your tests will tell you if something broke. "Unit testing" at the class level won't do that. Unit tests, integration tests and e2e tests are different things that solve different concerns. An e2e test will rarely expose an obscure bug within an implementation, and a unit test will rarely expose concerns in the global application flow. Both have their place. And they have nothing to do with the "interface vs not interface", IMHO.