News for hackers. Stuff that matters.
Re:Pretending OOP Never Happened by barrkel on Friday May 15, 2020 @01:04PM (#23194521)
Procedural code is quite popular in Java as used with IoC.

Global variables are called "Singletons". DI injects a reference to all the globals you need to do your work, so you don't feel so grubby. Often DI injects some kind of accessor to the database, a giant store of global variables.

Procedures are called beans, but you can tell they're procedures because they have a single public entry point and are usually called SomeKindOfVerber with a main verb() or do() or execute() method, they have the procedures they call (sorry, I mean "collaborators") injected, and they themselves are injected to where they'll be called from. Their state is ephemeral, and only exists for the purpose of executing a single flow of logic (this is called "bean scope").

All you need to do to describe your code as object-oriented is convert your procedures into beans, and tada!

Re:Pretending OOP Never Happened by commandlinefan on Friday May 15, 2020 @01:52PM (#23195126)
> Procedural code is quite popular in Java as used with IoC.

Yep, you get all the downsides of coding in Basic with the bonus of the performance hit you take from all the reflective instantiation.

Re:Pretending OOP Never Happened by apta on Friday May 15, 2020 @04:18PM (#23196918)
You don't have to incur the cost of reflection. Dagger2 for instance, is a compile time DI framework. Micronaut is also compile time.
Re:Pretending OOP Never Happened by sidlls on Friday May 15, 2020 @09:43PM (#23199698)
DI frameworks only encourage bloat, e.g., 1-to-1 interface to concrete classes. You know, for "unit testability" or "just in case" we need to implement the interface with a different concrete class later.

They can help simplify what is essentially a problem of complexity that is self-induced by the original decision to use OOP. But it's rare that they don't also introduce tons of additional other complexity, e.g., at runtime. Debugging these applications is one of the most infuriating things I've ever done as a programmer.

Re:Pretending OOP Never Happened by apta on Saturday May 16, 2020 @04:21AM (#23201479)
What kind of bloat are you talking about here? Binary and/or runtime cost? In the case of Java/JVM (and I believe C#/.NET) the JIT is almost trivially able to inline monomorphic calls, meaning that effectively, these interface calls are free.

Secondly, you don't even need to use interfaces with one-off implementations if you don't want. You can inject your concrete implementation class instances directly. Using interfaces just makes it easier to swap implementations as needed. The unit testability argument is completely orthogonal (and also in the case of the JVM, you can mock classes as they're open by default, so again, you don't even need to use one-off interfaces).

And I don't see how this issue is limited to OOP. At the end of the day, you're going to have to hook up your components and object graph anyway, either manually or using some kind of DI, so the complexity is always there. Even if you don't write interfaces with single implementors, non-trivial programs quickly become extremely verbose to implement. Maybe something like Scala's `implicit` can be of help here.

> Debugging these applications is one of the most infuriating things I've ever done as a programmer.

Again, Dagger and Micronaut do DI at compile time using codegen, so nothing to debug at runtime.

Re:Pretending OOP Never Happened by sidlls on Saturday May 16, 2020 @11:14AM (#23204035)
Compile time DI doesn't mitigate huge stack traces or using unnecessary interfaces, which both contribute to bloat.

Non-trivial programs don't have to be verbose. They are generally made so by the addition of unnecessary "features" that, in turn, require even more additions "to reduce complexity."

Re:Pretending OOP Never Happened by apta on Saturday May 16, 2020 @12:11PM (#23204644)
As I mentioned, you don't have to use unnecessary interfaces with DI (compile time or runtime). You can directly inject your implementation classes. Secondly, huge stack traces are not related to DI either, they're an orthogonal issue.
Re:Pretending OOP Never Happened by hagy on Friday May 15, 2020 @01:33PM (#23194865)
I agree with a lot of this, although I wouldn't conflate DI references with global variables. They certainly allow for shared state between different components, but the great thing about DI is that you can always create new contexts with a separate set of shared state references.

I also agree that is kinda of funny to constantly so the Verber classes everywhere in Java, but it has at least improved over the last several years with the standardization around Function<T,R>, Supplier<T>, Consumer<T>, etc. Lambda syntax and method references also help. I'd admit the syntax for using a functional style in Java is still a little jenky, but its quite an improvement from where things were back in the day.

Re:Pretending OOP Never Happened by barrkel on Friday May 15, 2020 @05:37PM (#23197775)
I'm being tongue-in-cheek, of course.

The biggest upside I see for automated DI is to get back concision once you've parameterized your code by the code it depends on. Usually this parameterization is done for unit testing purposes.

Direct calls are hard to exclude or intercept for testing, so you call a method on an interface or a base class or an object instead, and now you need to be initialized with a reference which may be replaced for testing purposes. But the requirement for initialization remains in production, even when there is normally exactly one candidate and it might not even require state if it wasn't for all the other references it in turn needs to call.

The great increase in the burden of initialization creates demand for the IoC / DI container. And once you have a container framework which takes care of gluing objects together, why not let it stick transparent adapters (proxies) around the objects too? Proxies for per-request state, multi-threading, transaction management, caching, all that good Common Lisp advice of before, after and around.

The temptation for clever people can be hard to resist, and before you know it your local code has global side effects unknowable to people who don't have the full picture in their head.

I actually blame unit testing, particularly when combined with a static type system. Static binding, in particular: you could mock static calls if dynamic binding like Emacs Lisp was available!

Unit testing is great at the leaves of the call graph, for self-contained things like container libraries, or things which behave functionally, i.e. they transform things.

Unit testing is like arthritis in the middle of the call graph. It makes rearchitecting much harder by baking in assumptions both upstream and downstream, and the easy temptation of mocking usually overspecifies the interaction with collaborators. The parameterization demands DI which encourages too much cleverness, as discussed. And unit tests don't leave you very confident that things will hold together because test mocks and stubs don't necessarily replicate the behaviour of the real things.

I like integration testing higher up the stack, and trying to convert a program as much as possible into a composition of functional elements - things that belong on or near the leaves of the call graph, and leaving as little "middle" as possible to be infected with unit tests.

Re:Pretending OOP Never Happened by oneplane on Friday May 15, 2020 @05:00PM (#23197338)
I agree and might add that having improvements on something imperfect (but a thing that does get real work done) can be worth much more than compsci perfectionism.

It is very hard to do something that is 'good' in technical terms while also being 'good' in product terms. While the discussion often doesn't revolve around the practical implications of taking steps toward a commercially uncommon language or paradigm during business engineering it is pretty much where all the 'stuff gets done'.

Re:Pretending OOP Never Happened by josephg on Friday May 15, 2020 @06:48PM (#23198432)
You can have your cake and eat it too. For example, react’s functional components are both good in product terms and good in technical terms. C#, JavaScript, python, swift and others have lots of tools for programming in a more functional style.

And java isn’t where all the ‘stuff gets done’ - well, outside of the enterprise programming bubble. JavaScript and (surprisingly) python are the workhorses of the modern startup ecosystem. I can’t speak with certainty about python, but separating logic and data, and using immutability with stateless components and functions is all very common in the JavaScript world.

Re:Pretending OOP Never Happened by oneplane on Friday May 15, 2020 @08:08PM (#23199025)
I didn't intend to sound like I meant that Java is where the stuff gets done but rather that 'stuff gets done' using non-ideal languages, frameworks, tools, context etc.

C, C++, Java, Python, JavaScript, C#, even PHP are not exactly ideal, even in their special niches, but a lot of real world stuff, if not most 'stuff' gets done using that.