Author Topic: Modern Java - Not OOP.  (Read 3075 times)

0 Members and 1 Guest are viewing this topic.

Offline paulcaTopic starter

  • Super Contributor
  • ***
  • Posts: 6354
  • Country: gb
Modern Java - Not OOP.
« on: March 14, 2025, 09:25:02 am »
I have seen this particular paradigm of coding and reflected before that it is not OOP in the slightest way.

However it wasn't until this week I spotted a direct conflict with "OOP design techniques" and "modern techniques".

Value Objects.

These are structs.

Handlers and deligates.

These are "Procedures".

It's procedural programming, al 1963.

Value objects contain data.  Procedures contain the code that operates on the data.  This is classical Procedural programming.

The thing I noticed was a junior programmer had rewritten some if/else constructs to be neater.  They also inverted the conditions from classical:

if( isTheThingAThing( theThing ) ) {}

into:

if( isAThingPredicate.apply(theThing) ) {}

Ala.... Functional programming (spit).

It's garbage.  Reads like garbage and is basically a Functional Programming hack on top of Proceedural dross in an OOP language.  The world has gone nuts.

The correct OOP way to do this is to encapsulate functionality with the data it operates on.

if( item.isAThing() ) {}

Is the OOP way.

However....  because of the procedural nature of modern Java with the likes of SpringBoot and Jackson ser/des you can't do this.  Why?

1.  All the value objects are autogenerated from the REST API Yamls.
2.  You can't add functionality to valueobjects, it's just not done.
3.  If you wanted to add functionality to a valueobject you will need to fight with the autogenerator and/or use "dark side" introspection and injection (also not OOP).
4.  If you did manage to get your "isAThing()" method into the valueobject, then because you called it "is..." it will be picked up by the bean serialiser as a "boolean" property and serialised into the output JSON/XML.  As isAThing() is a behavioural method... a derived entity... it is volatile and should not be serialized... so you have to hack that into the auto generated value object.

The short is.

Java OOP is DEAD.  The language has been overrun by UI Devs who want to turn it into Javascript and by JavaBean/ValueObject/Procedural modern ways.

Java is now better of known as "Javascript 2.0"

"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Online ledtester

  • Super Contributor
  • ***
  • Posts: 4115
  • Country: us
Re: Modern Java - Not OOP.
« Reply #1 on: March 14, 2025, 01:08:12 pm »

The correct OOP way to do this is to encapsulate functionality with the data it operates on.

if( item.isAThing() ) {}


You might need both approaches.

Suppose you have code which knows how to traverse some data structure -- could be a tree or a database or lines in a file, etc. It is natural to pass a predicate function to to this code to select or operate on only those items you're interested in. Suppose you are interested in Employees who have been with the company for 10 years and work in Distribution. Are you going to create a brand new method in the Employee class just so you can make this ad hoc query?
« Last Edit: March 14, 2025, 01:11:36 pm by ledtester »
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6398
  • Country: nz
Re: Modern Java - Not OOP.
« Reply #2 on: March 14, 2025, 01:55:43 pm »

The correct OOP way to do this is to encapsulate functionality with the data it operates on.

if( item.isAThing() ) {}


You might need both approaches.

Suppose you have code which knows how to traverse some data structure -- could be a tree or a database or lines in a file, etc. It is natural to pass a predicate function to to this code to select or operate on only those items you're interested in. Suppose you are interested in Employees who have been with the company for 10 years and work in Distribution. Are you going to create a brand new method in the Employee class just so you can make this ad hoc query?

Shirley you'll make a newEmployeeQuery10YearsInDistributionFactory() static method that returns a new subclass of EmployeeQuery with a specialised override of the queryFilter() method?
 
The following users thanked this post: Siwastaja

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17773
  • Country: fr
Re: Modern Java - Not OOP.
« Reply #3 on: March 14, 2025, 02:01:40 pm »

The correct OOP way to do this is to encapsulate functionality with the data it operates on.

if( item.isAThing() ) {}


You might need both approaches.

Suppose you have code which knows how to traverse some data structure -- could be a tree or a database or lines in a file, etc. It is natural to pass a predicate function to to this code to select or operate on only those items you're interested in. Suppose you are interested in Employees who have been with the company for 10 years and work in Distribution. Are you going to create a brand new method in the Employee class just so you can make this ad hoc query?

Shirley you'll make a newEmployeeQuery10YearsInDistributionFactory() static method that returns a new subclass of EmployeeQuery with a specialised override of the queryFilter() method?

Ex-act-ly! :-DD
 

Online tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Modern Java - Not OOP.
« Reply #4 on: March 14, 2025, 02:54:59 pm »

The correct OOP way to do this is to encapsulate functionality with the data it operates on.

if( item.isAThing() ) {}


You might need both approaches.

Suppose you have code which knows how to traverse some data structure -- could be a tree or a database or lines in a file, etc. It is natural to pass a predicate function to to this code to select or operate on only those items you're interested in. Suppose you are interested in Employees who have been with the company for 10 years and work in Distribution. Are you going to create a brand new method in the Employee class just so you can make this ad hoc query?

Shirley you'll make a newEmployeeQuery10YearsInDistributionFactory() static method that returns a new subclass of EmployeeQuery with a specialised override of the queryFilter() method?

Crap design is scarcely confined to OOP programs :)

If you haven't seen similarly crap digital logic designs (etc!), then you haven't been looking closely enough.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline paulcaTopic starter

  • Super Contributor
  • ***
  • Posts: 6354
  • Country: gb
Re: Modern Java - Not OOP.
« Reply #5 on: March 14, 2025, 09:02:17 pm »

The correct OOP way to do this is to encapsulate functionality with the data it operates on.

if( item.isAThing() ) {}


You might need both approaches.

Suppose you have code which knows how to traverse some data structure -- could be a tree or a database or lines in a file, etc. It is natural to pass a predicate function to to this code to select or operate on only those items you're interested in. Suppose you are interested in Employees who have been with the company for 10 years and work in Distribution. Are you going to create a brand new method in the Employee class just so you can make this ad hoc query?

Emmm.  no.

Any predicate is just a hidden if statement.  Arseholes who want to avoid if statements by using predicates are ... areseholes who don't understand what a predicate is, how it's implemented in the underlying  "primitives".

Similar arguments can be made for "imutable" and for polymorphism.  It's all just "branch <condition>".

IMHO if you don't fully understand .. or more importantly, if your TEAM do not fully understand the implications of the patterns they use, it's better they DONT use them.
« Last Edit: March 14, 2025, 09:06:55 pm by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Offline paulcaTopic starter

  • Super Contributor
  • ***
  • Posts: 6354
  • Country: gb
Re: Modern Java - Not OOP.
« Reply #6 on: March 14, 2025, 09:05:04 pm »

The correct OOP way to do this is to encapsulate functionality with the data it operates on.

if( item.isAThing() ) {}


You might need both approaches.

Suppose you have code which knows how to traverse some data structure -- could be a tree or a database or lines in a file, etc. It is natural to pass a predicate function to to this code to select or operate on only those items you're interested in. Suppose you are interested in Employees who have been with the company for 10 years and work in Distribution. Are you going to create a brand new method in the Employee class just so you can make this ad hoc query?

Shirley you'll make a newEmployeeQuery10YearsInDistributionFactory() static method that returns a new subclass of EmployeeQuery with a specialised override of the queryFilter() method?

All of these problems (they create) can be fixed.  "The Emperor IS fully clothed."
https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 
The following users thanked this post: Siwastaja

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17773
  • Country: fr
 

Offline paulcaTopic starter

  • Super Contributor
  • ***
  • Posts: 6354
  • Country: gb
Re: Modern Java - Not OOP.
« Reply #8 on: March 21, 2025, 11:27:39 am »
Speaking of "not OOP" and of "Black magic, hidden wizardry and voodoo".

The other prevalent "paradigm or pattern" in modern day Java, especially in "Spring framework" is...

AOP - Aspect Orientated Programming.

Made up pigeon example:

Code: [Select]
@WithFile(someResourceFactory)
public void writeContents( byte[] contents ) {
    context.writeFileBytes( contents );
}

The actual file handling code is encapsulated in to the "WithFile" annotation which is presumed to look at things such as the "context", who called it, what the contents are and make the correct file available for the writeFileBytes call.  Knowing how many bugs people introduce when handling files, you can see the advantages of locking normal devs out of it entirely.

Aspect orientation is basically the "Decorator pattern" on steriods using "Code injection" mechanics to wrap method calls with prefixes, postfixes and in some cases entirely proxy the method.  Especially common for "native code hooks" in Java for performance.

The places you will also have seen this used, it's most common use by far is in performance testing and metric logging.

"How long does this function normally take to execute?"

Many of these frameworks for Java, C++, Python etc. Use AOP injection.  The injection is the code required to instantiate the timers, start and stop them and log the data.  Which can then be switched on and off when needed while the application is running.  Importantly... without the code IN the actually method being anyway aware of it.... until it fails and you get the stack trace of WTAF!?!?!?

AOP can also be analogized to it's simplest form of C pre-processor directives which can also prefix and postfix methods... for similar reasons.  Even hijack stack and state etc.  Performance metrics, white box testing, low level unit testing, exposure of private state for testing etc. etc. etc.  All usecases.

Audit logging.  The other defacto use case of AOP, in higher security applications.  Annotate a method with a tag which causes all calls to it to log the global security principle and identities in use when it was called, where it was called from, when, who by, etc. etc.  Not just on entry, but on exit.  This has and will continue to catch escalation exploits in flight.

I'm not for or against it as it does provide a very sharp tool for many circumstances.  However it also produces close to "non-debug-able" code.  I got a stack trace yesterday and out of the 450 depth of calls, 3 were in my code.  At least half of them where in native stubs to native SO libraries and the only way to see what they were doing was to decompile the stubs in the IDE.  From that you got:  byte[] param_1, byte[] param_2 and so on decompiler garbage.

AOP injections and debuggers don't play very nice either.  Part of the reason for this is that different AOP annotations have different "scopes".  Some of them will be executed and applied when the class is loaded, some when an object is instantiated and some when the method is actually called.  Most IDEs by default just ignore them entirely when you step through, it's only when you step INTO a method call that you end up going down the AOP stack trace of horror.
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Offline Fixpoint

  • Regular Contributor
  • *
  • Posts: 105
  • Country: de
Re: Modern Java - Not OOP.
« Reply #9 on: June 01, 2025, 10:09:25 am »
Value Objects.
These are structs.
Handlers and deligates.
These are "Procedures".
It's procedural programming, al 1963.

It depends on how they are used. In this generality, the statement is not correct. Of course, I think I get what you mean -- if this pattern is applied everywhere, it doesn't look good.

Quote
Value objects contain data.  Procedures contain the code that operates on the data.  This is classical Procedural programming.

Yes.

Quote
if( isAThingPredicate.apply(theThing) ) {}

Ala.... Functional programming (spit).

This has its place in certain scenarios, namely when you need an abstraction of a predicate in order to decouple it from concerns it is not related to. It represents abuse when a coupling is created instead of broken. You did not provide enough information to decide on this.

This by itself does not constitute functional programming; of course, it looks similar, but FP at its core stipulates more. In particular, you would not necessarily be allowed to write "if" in the first place.

Quote
if( item.isAThing() ) {}

This might be exactly right. But as I said, you did not provide enough information to decide your particular scenario.
« Last Edit: June 01, 2025, 10:14:23 am by Fixpoint »
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 4832
  • Country: us
Re: Modern Java - Not OOP.
« Reply #10 on: June 06, 2025, 06:44:28 am »

Emmm.  no.

Any predicate is just a hidden if statement.  Arseholes who want to avoid if statements by using predicates are ... areseholes who don't understand what a predicate is, how it's implemented in the underlying  "primitives".

This whole thing is just absurd strawman arguments.  Of course everyone knows it's implemented with an if statement.  Although the predicate itself isn't the if statement, the predicate is a function or function like object that returns true or false, which is typically used in an if statement instead of an inline operation.  The predicate is the boolean function not the if statement.

Again.  The point of a predicate is to decouple the condition from the logic that decides where to apply the condition and/or from the logic that decides what to do to with things that match.

People can use it in an inappropriate situation or do so badly.  Sometimes the coupling is natural and pretending to decouple is just extra obfuscation.  Obviously.  The fact that programmers sometimes do a bad job is not news to anyone. 
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 4832
  • Country: us
Re: Modern Java - Not OOP.
« Reply #11 on: June 06, 2025, 07:32:35 am »

The correct OOP way to do this is to encapsulate functionality with the data it operates on.

if( item.isAThing() ) {}

Is the OOP way.

That is an incorrect statement.

What makes something OOP is that it abstracts it's contents with an interface that defines how you can interact with it.  Whether isAThing() belongs as a method is a matter that can't be determined without examining the semantics of item.

As long as it's isAThing() only uses the public interface of item, it's completely consistent with object oriented programming to have it a free function, a member of a different type, or a type of it's own.

What would be discouraged by OOP is to have it be a friend function, or for item to unnecessarily expose implementation publicly.

There has been more return to procedural styles in recent years.  A number of factors contribute to that.  One is the observation that the vast majority of interfaces only have only a single inplementation.  Languages have added functionality to unify syntax  and make it easier to add abstraction after the fact so you don't need to decide up front if you might ever change an implementation. And refactoring techniques have improved to allow large scale changes if needed
 

Offline Marco

  • Super Contributor
  • ***
  • Posts: 7742
  • Country: nl
Re: Modern Java - Not OOP.
« Reply #12 on: June 06, 2025, 08:21:16 am »
Why not just use instanceof?
« Last Edit: June 06, 2025, 08:30:56 am by Marco »
 

Offline paulcaTopic starter

  • Super Contributor
  • ***
  • Posts: 6354
  • Country: gb
Re: Modern Java - Not OOP.
« Reply #13 on: June 06, 2025, 11:43:40 am »
That is an incorrect statement.

What makes something OOP is that it abstracts it's contents with an interface that defines how you can interact with it.  Whether isAThing() belongs as a method is a matter that can't be determined without examining the semantics of item.

The history was.... 

if( item.getPropertyA() == 'F' )

Style conditional in the code.  However, later it needed to become more like:

if( item.getPropertyA() == 'F'  && item.getPropertyB() < 1 )

So, my proposal was to wrap this in an "item.isAThing()".

This is a standard pattern.  A standard naming convention which is taught in every single OOP course I have done in the past 30 years.  Including those originating in SmallTalk.

Whether item is a thing or not is a property of item.  Item can decide without concerning the consumer how.

What induced the rant is that adding functionality to a SpringBoot + OpenAPI + Jackson + Native underlyer - auto generated - modern Java Springboot microservice ...  detonates spectacularly.

The statement, that that setup is not object orientated is pretty solid.  It's opinionated and optimised for it's primary purpose.  To make it fast enough it uses native C code libraries for most of it's data struct processing under the hood.  Turns out that parsing JSON in Java is slow and uses a lot of memory, funny that.  So they do it in native libraries.  At this stage we have C structs and delegates and very little else.  Just layers of 400 deep call stacks you normally get in Java.

The "Java Bean Specification" then gets used and abused, including that said "naming convention", such that the method declaration prefix "is...." is considered a "Property of type boolean".  This causes the underlying layers to serialise it as a non-transient....in the JSON and down the ORM stack causing many a nasty stack trace.

In a POJO become Java Bean, you can easily differentiate and/or annotate / flag transient to prevent serialisation and still allow both producer and consumer to call "isAThing()" assuming they import the class def.  This presents the transparency almost like an "RPC" by appearing to "transfer functionality" with the data.  In my opinion this fits more with the OO model.

EDIT:  I want to point out I am not an OOP priest.  I spit in the halls of SOLID as a utter fucking waste of billions of dollars in almost ALL cases of it's application.
« Last Edit: June 06, 2025, 11:46:08 am by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Online radiolistener

  • Super Contributor
  • ***
  • Posts: 5730
  • Country: Earth
Re: Modern Java - Not OOP.
« Reply #14 on: June 06, 2025, 11:54:04 am »
Ala.... Functional programming (spit).

It's garbage.  Reads like garbage and is basically a Functional Programming hack on top of Proceedural dross in an OOP language.  The world has gone nuts.

The correct OOP way to do this is to encapsulate functionality with the data it operates on.

in real-world programming, there’s rarely a single "correct" way to do things - especially when it comes to paradigms like OOP, functional, or procedural programming. These are tools, not dogmas, and they often complement each other.

Using predicates or other functional-style constructs in OOP code isn’t a "hack" - it’s just one of many ways to express intent clearly. What really matters is writing simple, readable, and maintainable code. That may sound easy, but it actually is not and takes a lot of experience and maturity. Experienced developers often lean toward simplicity because they’ve seen how complexity costs time and clarity.

On the other hand, newbies can sometimes over-engineer things in the name of "proper OOP" or modern trends, blindly applying patterns or paradigms without fully understanding their purpose - just believing "this is the right way" because it follows some principle they’ve read about.

In the end, good code isn’t about demonstrating theoretical purity - it’s about solving problems cleanly and understandably.


On one hand, you can design an elaborate hierarchy of classes that strictly follow object-oriented principles, but end up with bloated and overly complex code. In such designs, even simple tasks may require navigating deep inheritance chains, applying numerous design patterns, and carefully fitting into a rigid structure.

On the other hand, you can write straightforward procedural code that’s easy to read, easy to test, and easy to modify when requirements change. Sometimes simplicity and clarity bring far more value than architectural perfection.

You may have encountered situations where someone implements a solution that technically works, but the resulting code is so complex and convoluted that it's difficult to understand or maintain. Then someone else comes along and replaces it with a much simpler, more elegant version - something that's clear, readable, and easy to extend.

At first glance, the second solution may appear trivial. But in reality, it often reflects deeper experience. It likely came from someone who has solved similar problems many times before, who took the time to thoroughly analyze the root cause, observe every potential pitfalls during debugging, and refine the solution through multiple iterations - ultimately arriving at a simpler and more maintainable design.
« Last Edit: June 06, 2025, 12:20:51 pm by radiolistener »
 

Offline paulcaTopic starter

  • Super Contributor
  • ***
  • Posts: 6354
  • Country: gb
Re: Modern Java - Not OOP.
« Reply #15 on: June 06, 2025, 12:05:39 pm »
Why not just use instanceof?

This would be frowned upon as it's usually a sign you have incorrect polymorphism.  If your classes are correctly setup, you shouldn't need an instanceOf.

If "item" is-a Thing, then simply referencing it as a Thing makes it a Thing.  So calling "doStuff()" on it does not require a conditional.

However, sometimes this would require adding 50 lines of boiler plate architectural code that does nothing to satisfy.  So in those cases instanceff is find in my book. :D  Needs must before satisfying pedantry.

EDIT: Last time I went down this rabbit hole I didn't know in advance what things I might need later.  So I couldn't use instanceof or I would need to update that if/else with another elseif ... instanceof.

The rabbit hole leads to the Visitor pattern.  Probably one of the most obnoxious (and annoying useful) OOP patterns.... if it even is a OOP original and not a borrow in.
« Last Edit: June 06, 2025, 12:22:40 pm by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Offline paulcaTopic starter

  • Super Contributor
  • ***
  • Posts: 6354
  • Country: gb
Re: Modern Java - Not OOP.
« Reply #16 on: June 06, 2025, 12:15:48 pm »
in real-world programming, there’s rarely a single "correct" way to do things - especially when it comes to paradigms like OOP, functional, or procedural programming. These are tools, not dogmas, and they often complement each other.

Using predicates or other functional-style constructs in OOP code isn’t a "hack" - it’s just one of many ways to express intent clearly. What really matters is writing simple, readable, and maintainable code. That may sound easy, but it actually is not and takes a lot of experience and maturity. Experienced developers often lean toward simplicity because they’ve seen how complexity costs time and clarity.

On the other hand, newbies can sometimes over-engineer things in the name of "proper OOP" or modern trends, blindly applying patterns or paradigms without fully understanding their purpose - just believing "this is the right way" because it follows some principle they’ve read about.

In the end, good code isn’t about demonstrating theoretical purity - it’s about solving problems cleanly and understandably.

This I can agree with and it is those "newbies" and "trend followers" which do actually bug me.

Priorities:
1.  Working software.
2.  Clean/maintainable software.
3.  Optimised software.
4.  Academically correct software.

I think a lot my frustration is less about applying different paradigms and more about inappropriately applying them, ESPECIALLY, when mixing them.

I find when you start mixing all these paradigms together you inherit (pun) all of the downsides and very often cut yourself off from the advantages.

Spring being procedural is out of "needs must".  Industry wants to use Java for it's verbosity and prescriptive-ness (less surprises later), but industry wants lightening fast speeds and REST endpoint marshalling done in microseconds not milliseconds.  So they introduce native layers as soon as possible. 

However, in a modern code base today in Java Spring, you will find botched functional code everywhere.  You will find procedural patterns.  You will find OOP, you will also find layers of "zero function" architectural code to meet some pet fettish for "SOLID" or what not.

I just feel that if Java programmers can't seem to get a single paradigm correct, why should they start adding more to do incorrectly?

Examples of dumb functional code...

streaming a list... filtering it....updating propertyX.. streaming it again.... filtering it... updating propertyY....   4 times.

The Java implementation of these primitives does not have a proper DAG optimiser.  It will iterate the list 4 times.  It will create 4 (or a factor of 4) intermediary immutable copies of the collection.  If it is later upgraded to a parallel stream it will corrupt if property X and Y depend in anyway.  The filter was the same in all cases.  The only reason this code existed is because the dev didn't know how to add multiple updates in the expression, so he/she copy and pasta'd the expression 4 times.
« Last Edit: June 06, 2025, 12:20:00 pm by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf