#On proper design and coding

43 messages · Page 1 of 1 (latest)

arctic lichen
#

I started working with Java relatively recently and would like to know if there is any literature that discusses examples of when abstractions actually make things worse. I’m not referring to the usual "reducing code duplication" or similar basic principles; rather, I want to understand where abstractions incur a high cost and how to minimize that. Also, regarding design patterns — are there any fundamental ones? (I don't mean patterns for specific niche situations, but rather everyday ones—things that come up very frequently regardless of the project itself)

P.S. I would welcome any opinion and I don't use AI for writing or studying

astral cloakBOT
#

<@&987246883653156906> please have a look, thanks.

#

Here is an AI assisted attempt to answer your question 🤖. Maybe it helps! In any case, a human is on the way 👍. To continue talking to the AI, you can use </chatgpt:1108714622413963314>.

#
TJ-Bot
I started working with Java relatively recently and would like to know if there is any literature that discusses examples of when abstractions actually make things worse. I’m not referring to the usual "reducing code duplication" or similar basic principle

Abstraction is a cornerstone of good design in Java and OOP generally, but it’s not an absolute good in every context. When abstractions become too complex, too generic, or are applied prematurely, they can introduce unnecessary indirection, reduce readability, and make the code harder to maintain or debug—sometimes called “abstraction overuse” or “abstraction inversion.”

Key points discussed in the literature include:

  • Premature abstraction: Abstracting before understanding the real problem often leads to forced hierarchies and interfaces that don't fit well, increasing complexity.
  • Over-engineering: Creating abstractions "just in case" rather than for immediate needs can increase cognitive load, making codebases brittle.
  • Leaky abstractions: When abstractions hide complexity imperfectly, it can confuse users of those abstractions, deteriorating rather than improving clarity.
  • Performance implications: Sometimes abstractions add runtime overhead or complicate optimization.

To deepen your understanding, look for literature that contrasts pragmatic design versus over-abstracted design, focusing on when to favor straightforward, concrete solutions instead.


Useful links:

arctic lichen
#

I think I used the word "literature" incorrectly in this context...

astral cloakBOT
#

For learning Java, we recommend the ebook Modern Java:
https://javabook.mccue.dev/

It is completely free, meant for beginners and covers all content relevant for the first year. It is a great way to learn Java from the ground up.
If you run into any questions during your journey, you may ask us in #1051826284008853505 and we will help cat_thumbs_up

If you prefer a more traditional learning experience, we also recommend MOOC:
https://java-programming.mooc.fi/
This is the course used by the University of Helsinki to teach their students programming in their first year, also completely free.

dawn gulch
#

this is a good online book

arctic lichen
#

oki, ty

willow cairn
#

abstraction is pretty general a term, do you mean inheritance?

arctic lichen
#

Basically, I’m referring to OOP itself and the situations where it might be preferable to replace objects with something simpler

willow cairn
#

because if it is, it would be always.
you are better off with composition in nearly all cases

willow cairn
#

thing is, oop do not really exist in java, it can be done over any language

arctic lichen
#

the very division of code into classes and objects

arctic lichen
willow cairn
#

when you say high cost and how to minimize that
do you mean cpu cost, dev cost in time,dev cost in complexity?

arctic lichen
#

processor resource consumption

willow cairn
arctic lichen
#

Oh, so that's what you meant...

willow cairn
arctic lichen
#

very superficially

#

I haven't really read much about it; I've mostly stuck to the "code" level

willow cairn
#

well, if your code always do spontaneous things one after another there is not really a trick to it,
but if you can separate your code in steps, and each step can be done in a loop on many many elements,
if these elements are in the same continuous memory region, the cpu can be faster, as the ram can be better loaded to the cpu

#

you can think of it as your desk, if sheets of paper are on your desk and you need to do something on all of them one after another,
it is better than if you need to do the same thing on 1 sheet of paper that iarein each house of your city.

#

the difference in time is that huge, a factor 100-1000

arctic lichen
#

A link to an article or literature covering this might be enough for me

coarse fog
short owl
#

When java first came out in the 90's it was very oop oriented as that was very popular back then. The current idiom the Java architects have been working on is still OOP but mainly for defining borders of your codes functionality, and implementing more code in the functional style. Design with OOP, Implement functionally. That means reducing state (class member variables) to what is necessary and embracing immutable data types. The java maintainers want to maintain and increase performance as best they can with the Java model of having java code compile to a intermediate code (byte code) and then have the JIT optimize that best to machine code.

The JIT (just in time compiler) that is the workhorse of the JVM can optimize and work better when it's executing code that it can prove doesn't have side effects. That's usually thrown out the window if data is reachable by multiple threads since there is no enough time ('Just in time' is the name) to deduce if other threads were involved since the last time it executed a segment of code.

What does this mean. It means breaking up your code in to functional methods (static if they can be) and letting the JIT try to inline these methods back into executable code that it can reason won't require extra checks on state validity under the hood.

#

There's also an embrace of 'declarative code', which means what the code does and not how the code does it. For example, sorting. We have api's for sorting in java. There's no need to know how they work under the hood really but we do know that we don't want the API endpoint for sort to change its signature just because some new sorting algo comes out that is slightly different. (You can view the java source code for sort and see they choose many opinionated defaults settings.) I see OOP in java as really a reason on where to break down your declarative code.

So in a nutshell, (to help the JIT work better), Reduce the state of scope, write declaratively, write smaller methods (they inline easier), write implementations functionally.

arctic lichen
coarse fog
#

you can reasonably argue for any abstraction depending of the context or your ideas

#

and any single abstraction isn't bad

arctic lichen
# coarse fog and any single abstraction isn't bad

It seems to me that the context of the question was lost.
As I wrote above: "Basically, I’m referring to OOP itself and the situations where it might be preferable to replace objects with something simpler"

A very vague example, but an example nonetheless:

for (Entity* e : scene.getAllEntities())
    e->getTransform()->update(dt);```
However, each object resides in its own memory area; cache misses occur, and the virtual call prevents the compiler from inlining—and consequently vectorizing—the loop.
more stably:
```c++
for (size_t i = 0; i < count; ++i)
    positions[i] += velocities[i] * dt;```
coarse fog
#

i am telling you why it is bad when you spam it too much

arctic lichen
#

Oh... I apologize; I missed your point while using the translator...

#

sorry

willow cairn
arctic lichen
#

I know; thanks for the additional source. I just cited the first example I remembered well

short owl
# arctic lichen It seems to me that the context of the question was lost. As I wrote above: "Bas...

This is a pretty good example scenario to examine. Basically you are arguing Array of Structs VS Struct of Arrays. Though you may be right about performance, you shouldn't, in general, second guess the JIT. Java has jmh which is a specialized performance test harness which you would test a theory like that.

In your second example,

for (size_t i = 0; i < count; ++i)
    positions[i] += velocities[i] * dt;

It is known that if positions and velocities are member variables of some (Struct of Arrays) object, that in this case performance will be poor because, as I mentioned, unless the JIT can defacto prove no other thread can access those arrays (which it's not designed to because of time constraints) it's forced to do null checks on both those arrays each access. There's other reflection issues in Java why these arrays can't be trusted but they are working on that also.

Java architects mostly solved this issue with enhance-for which you used in your first example but can't be used in this Struct of Arrays style programming because it's only acting on one array at a time.

The correct way to do the second example in Struct of Arrays is assign to a local variable (which no other thread can access)

float[] pos = this.positions;
float[] vel = this.velocites;
for (int i = 0; i < pos.length; i++) 
  pos[i] = vel[i] * dt;

Personally I like arrays in Java and think Struct of Arrays is a valid structure... for hot code. But it's not the norm. It's the opposite of declarative. The java mentality is "do the declarative, java will eventually match performance down the road" and that has been proven correct just in the last five releases and just regarding array performance in general.

arctic lichen