But mostly unit testing. The commitment to isolated testable units of code is what generates abstractions of dubious value.
Don't use a class where you could use an interface: easier to mock. Don't use a static method where you could use an instance method on a collaborator: easier to inject, replace and mock. Your object graph getting too hard to compose, or other design smells? Don't worry, we have tools to handle the complexity, so you don't have to try and reduce it.
it's funny since in theory TDD should reduce API surface as it is supposed to make people think about API before implementation. But then , TDD and DDD intersect and it's not much TDD anymore when the domain model has to be implemented...
Fake: A replacement module that behaves like a production module, but with certain simplifications, for example in-process vs. using rpcs, or simply cleaning up the filesystem after usage. For example, https://github.com/tk0miya/testing.postgresql. "automatically setups a postgresql instance in a temporary directory, and destroys it after testing".
Furthermore, these are external dependencies that can't be run in-process. If a dependency can be run in-process (aka library), there is no justification to ever mock it. I've even seen codebases that mock their own class B in order to test class A. Run the production code already. Ban mocking libraries.
Unit testing with mocks is another form. One does not replace the other.
Mocks usually over-specify implementations by setting up expectations of a specific implementation conversation rather than an outcome. They're painful to debug after refactoring implementation - they end up write-only - and they generally inhibit refactoring of the mocked API - often mocked instances of APIs outnumber production invocations.
I try to encourage people to replace control flow with data flow where possible; messages, command objects rather than method calls; iterators, streams and consumers composed together, rather than loops. Data flow can normally be trivially redirected into a container, and if the data objects are simple inert immutable tuples, they're trivial to construct as inputs or assert against on outputs.
Fakes are good too, better than mocks in most situations, since they are easier to refactor.
Although, IME, integration tests, while slow and often brittle (especially if you have any async components and define failure conditions in terms of timeouts), have a significant upside in permitting large factoring while still being able to test a substantial amount of end result functionality (i.e. the stuff that matters, not implementation details).
> intercept and rewrite code at execution time
I think "execution time" is one of the big issues, a lot of these patterns evolved from application server middleware like glassfish that would introspect and run packages and often provide services at runtime, sun and IBM were busy selling licenses and support for these applications servers so these abstractions were great for their bottom line.
Take away the execution time requirement and injects mocks/fakes/stubs at compile time suddenly you don't need interfaces to make mocking easier, static methods are easy to mock and you probably don't need DI/IOC.
You're right that Java had some long class names to start with, but I think it was mostly Spring that introduced the PerhapsOverlyDetailedClassNamingConvention.
Spring's introduction and subsequent wide adoption was a big driver of TDD's growth, which encouraged all those interfaces and mocks.
You can have a bean. Great. You can have a bean that's a singleton. Also great. Then you discover that you can have two different beans that are singletons, and you find that they have one thing in common: how the single instance is created and accessed.
Each such singleton bean will have about five lines of code that deal with the single instance, and these five lines will be largely similar.
So the Enterprise Java Architect sees that there is something to be abstracted, goes to town, and now you can have seven lines of code configuring the abstraction, so that you can avoid writing the five lines of boilerplate.
Unfortunately, the seven lines of configuration will be largely similar for different singleton beans...
Yeah, as a (mostly, currently) java programmer this is where the whole "framework" ecosystem loses me.
Dependency injection - absolutely. Really useful, I can have a setup method that reads config from the environment or wherever, sets up various things and passes them in (you need an SSL context associated to these CAs? Great. And you need one that needs mutual authentication? OK then, here's one I made earlier) . Makes testing easier, and makes it obvious where the setup is being done and how this stuff hangs together.
But 'full' IoC with a framework injecting stuff at runtime? Why? This just obfuscates as far as I can tell, and works around the language, making it harder to conceptualise and reason about.
(Also yes, I don't subscribe to interface-itis).
The argument for static typing is often so that refactoring is easier and ide's work better.
I always thought it was funny that the core reason for needing fancy ide's and large rename and refactor capabilities was that static typing and over abstraction in the first place.
As it stands, the necessity to relax members to `public` all over the place drives a giant bulldozer through any remaining delusions that Java is a language where actual OOP can be achieved.
But I agree that Java could use better unit test and mocking support.
For example, C++ has a culture of caring about low-level details to squeeze out maximum performance. I once found myself writing something that wasn't performance critical in C++ due to a library I needed, and just minutes later I caught myself reading up on move semantics for maximum efficiency. I couldn't help myself getting sucked in by the language culture!
Java's culture, in my opinion, is one of abstractions. There's little in the language to force you down that path, other than decades of it being the prevalent way Java is written, which bleeds into the standard library, core frameworks, and becomes the Java Way of Thinking.
There's an architect programmer and a worker bee. Everyone wants to be the architect and to create the "enterprise" framework used by the worker bees.
Yes exactly, that's why I personally stay away from Java. The language is totally fine, the tooling is also fine, performance is great, there's everything there in theory to build and maintain great projects, however, the culture is atrocious and overengineering is everywhere. And that's very hard to go against decades of culture momentum going this way.
Haskell's culture is also one of abstractions, but of a very different kind of abstractions than Java.
Actually I rarely remember java class names or methods, I just know a heuristic shortcut on how to find them.
After 15+ years of Java, I don't even see the boilerplate code, my brain ignores it like it ignores adds on webpages.
And you don't see this as an indication that reducing code size is important?
Then, I just think, excluding some horrendous language and some corner cases, experienced programmers are limited by their brain and not the programming language, just like certain languages that are more verbose are spoken faster and terser languages are spoken slower, but the information flow is done at the same speed.
Because having to write de facto one file per class does not really allow you to write very elegant software. It forces you to write files over files and no one wants that, so they fall back to primitive types.
For the abstraction bloat it is actually similar. Here the reason is that for the longest time (and even nowadays) Java makes it painful to work with functions. So now instead of just a plain function, you have to create a class with an interface that has one method... and instantiate it. Great... not.
I don't blame the language for this.
It wasn't that hard to read/write objects using JDBC (it did usually create a lot of boilerplate)
Please name a major programming language that has built-in ORM features -- a library is the best place for this sort of stuff.
For Java this was J2EE/EJB (stupidly over-engineered) and Hibernate/JPA (decent if you avoid the footguns). For Ruby you have ActiveRecord, Python has Django and SQLAlchemy, etc.
And of course you can... purely in terms of organizing building blocks most object orientated languages are pretty much a super set. I think the problem is most people are not consciously aware that object orientated approaches are just a pattern, and like any pattern you shouldn't apply them to everything. Being built in to many languages gives the impression they are as fundamental as a loop or a function - those are technically still abstractions, but far more primitive and less subjectively applicable... Object oreanted patterns feel like they are in between domain specific and basic building blocks, the higher you go the more polarizing these become, trying to use a DSL will seem absurd for one thing extremely elegant for another - when classes fit they are beautiful, but when they are carelessly used as a default, they are annoying bags of loosely coupled functionality and data that only serves to obscure relationships.
I'd speculate that the prevalence of poorly applied OOP is more to do with legacy and culture of the language along with the focus on how to use the language for newcomers.
there's shiittton of focus on architecture, patterns, interfaces and generally testability
there's shitton of discussions about those topics in those langs
So it may be reason why people tend to over abstract stuff.
As a younger dev, it is really difficult to fight against these seniors. Most seniors that care have left, they got the whole market begging at their feet. Other seniors have given up. There's a small number of seniors that still care, and there are layers of bureaucracy between suggesting a change, incorporating it, and seeing the results. It isn't unheard of to have half a year pass by from the suggestion of a change to the first signs of its effect. For many a young dev, that's half a year away from moving jobs without falling under immense scrutiny.
Abstractions that do not fix any of PHP's problems by the way (like its chaotic std lib, its weird mix of dynamism and rigidity, bizarre alt syntax for control flow, all the error reporting cruft, adhoc variable creation as side effect of certain function calls, context-less output buffers, antiquated scoping rules...), PHP is just adding features to the language without removing the bad parts. The irony, it's so bad as being a templating language PHP devs are using another templating language on top of it(smarty,...).
Standard java libraries like java.lang, java.util, java.util.concurrect, java.net, java.io, java.nio etc, hardly suffers from that part.
This is the language - quite straightforward, hardly any getter/setters - relatively simple hierarchy and so on. Then java.awt and later swing, java.bean came with more getters/setters, deeper hierarchies. Those are defunct now of course. I'd blame it on the idea of having visual UI editors to cause the extra fluff.
Then xml, spring and all the friends came. Java got the reputation. Still I'd argue the core java packages don't suffer from deep factory-of-factory jazz. (I avoid most of DI and resent mocks in tests)
That's mostly just unpleasant though, beyond that it seems to be something deeply ingrained in the culture, even something as simple as a structure (a simple class with public fields) is a heresy in the java world, you need to make those members private and add getters/setters or you're likely to get warnings from the IDE and probably not pass code review. The enterprise world builds more and more abstractions on top of this but the problem is very deeply rooted.
A good part of the problem though is just programmers trying to solve hard meta problems instead of boring repetitive business problems.
J2EE 1.0 came out, and was both over and under engineered (baroque and unworkable, which is ironic given the 7 fallacies come from their employer). And then Design Patterns came out, and it was all over but the crying.