Hah, so my problem isn't an API problem, it's just a basic OOP/DRY basics problem.
Yeah, I think I see what you are saying. And what needs to be done. Ever since Java 8, interfaces have become more and more powerful that I just don't use abstract classes anymore. The interface alone has been good enough for most of my uses cases. I haven't written an abstract class in over a year now, for example. But yeah, you are right -- I've just been using the wrong tool for the job here. A couple abstract class with a few utility methods (and maybe an empty list to store subtasks) would have turned this problem into a mild inconvenience at best. Lol, I'm still going to be spoiled and say I'd like the factory method anyways. But yes, I think the API as is is sufficient for making the necessary joiners I need. Thanks. On Sun, Aug 17, 2025, 6:19 PM robert engels <reng...@ix.netcom.com> wrote: > Exactly, in my experience if you are creating lots of classes with slight > differences you are not only missing an opportunity for DRY, but you are > increasing the cognitive load substantially - i.e. understanding the code > base (without lots of jumping around). > > I haven’t dug deep into your use case, but it sounds like an > XXXErrorHandler(s) based anonymous class(es) with properly defined > extensions/callbacks would allow a code reader at the site to see the > specialized handling these requests take - rather than trying to name all > of the scenarios and then have to understand them and remember what all of > them mean. > > It does make testing a bit different - but I may be an outlier here - and > I think more black box testing is beneficial anyway - rather than trying to > test each handling implementation (if the interfaces/bases are properly > designed, the extension code should be trivial and hard to mess up). > > On Aug 17, 2025, at 4:46 PM, David Alayachew <davidalayac...@gmail.com> > wrote: > > Can you give an example? I used Anonymous classes in my solutions, but > most of them were just based off of the Joiner interface. > > I guess I could introduce some state into the abstract class, and then > just tweak it for my needs. And obviously, I'd need a new abstract class > for each "category" of joiner. Then from there, just implement it inline, > making each inline case cheaper. Is that what you are hinting to? > > On Sun, Aug 17, 2025, 5:28 PM robert engels <reng...@ix.netcom.com> wrote: > >> Isn’t this a perfect use of an anonymous class with a base class? >> >> On Aug 17, 2025, at 4:00 PM, David Alayachew <davidalayac...@gmail.com> >> wrote: >> >> > I will guess that #1, #2, and #5 are relatively >> > simpler Joiner implementations, is there really >> > any benefit to use composition or inheritance here? >> >> Sorry, I have been unclear. Let me clarify. >> >> Yes, it is not hard at all to implement #1, #2, and #5 at all. But I >> don't have 5 joiners. I have well past 30 of them. >> >> Most of my joiners are very similar to each other. The 5 I showed you are >> most of the major "categories" of joiners I would make. But each category >> would have many derivatives of it. >> >> For example, #2 had a derivative that was the same, but for multiple >> Exception types as opposed to just one. Another derivative of #2 would go >> past a type test, and actually look at the fields of the Exception (like >> HTTP Error Code). Yet another would go one level deep, in case the >> exception was wrapped (like wrapping an HTTP Exception in a >> RuntimeException). >> >> So I started making a whole new class each time I wanted to do almost the >> same thing. But you start to run out of names that accurately describe what >> you are doing. And yeah, some joiners are going to be heavily reused, so it >> makes sense for them to have a whole type (and maybe source file). But many >> won't either. >> >> Once I realized that I was making a bunch of the same thing with minor >> variations, that's when I started thinking about inheritance and >> composition. Sorry if I made it sound like that's what I first jumped to. >> No, I thought about inheritance and composition because those are usually >> the default answers to the question of "How do I do what T is doing, but >> with a minor variation?" >> >> But inheritance and composition didn't get me very far for these joiners, >> which is what I was trying to say in my original email. Inheritance with >> state is error-prone (from my experience). And composition meant that I was >> making my code brittle. What if those methods I am depending upon need to >> change? >> >> So I went back to making each joiner be its own thing. In reality, most >> of my custom Joiners were either a simple record implementing the Joiner, >> or an anonymous class. Records are fine, but you start to run out of >> reasonable names when you have 5 different records that do close to the >> same thing. I kind of found a compromise by creating the record Joiner >> inside the method itself that I am working in (or the class if other >> methods need it too). That way, it's scoped off from the rest of the world. >> But considering how many I was making, it felt like a clunky solution. I'm >> fine peppering my code base with inlined records all over the place *as >> long as those records don't have a body*. But once they do, it starts to >> get annoying, and makes the code harder to read and skim. >> >> From there, I thought about making a factory. You know the rest of the >> story. >> >> > For #4 and #5 then its surprising that there is RPC >> > or split/join in the onComplete method. The >> > onComplete method is called with the completed >> > subtask and any exception/error executing >> > onComplete isn't going to change the subtask >> > status. Is there a reason you've chosen to put >> > that code there rather than in the subtasks? >> >> Yeah. Long story short, if that RPC call or the nested scope fails, well >> the literal goal that I created this scope to do (contruct an object) has, >> in effect, failed. That's grounds to just throw an exception and see if >> someone upstream can handle it. Maybe a retry or something. >> >> To me, it felt like I was keeping inline with what onComplete was trying >> to do -- sort of be an AOP-like post-processing joinpoint. If the contents >> of onComplete fails, well then the goal was unattainable anyways, so >> killing the scope via thrown exception doesn't feel wrong. And the >> exception will propagate, so it felt like I was following right along with >> how the spec intended things to go. Granted, I certainly am marching on the >> edge here, I'll concede that. >> >> > (for his API then the question as to "where" to >> > put code is a good discussion as it may not be >> > always obvious whether to code should execute in >> > the subtask, in the Joiner handling subtask >> > completion, or in the main task in the processing >> > after join. >> >> I would love a short guide on what code to put in what place. This looks >> to be a pretty integral API for handling a large number of tasks moving >> forward, so I see value in it. >> >> >> On Sun, Aug 17, 2025 at 1:13 PM Alan Bateman <alan.bate...@oracle.com> >> wrote: >> >>> On 16/08/2025 20:23, David Alayachew wrote: >>> >>> : >>> >>> Sure. Let me highlight 5 of them. Let me know if you need more examples >>> -- I have about 30+ custom implementations. >>> >>> >>> Thanks for sharing this selection. >>> >>> I will guess that #1, #2, and #5 are relatively simpler Joiner >>> implementations, is there really any benefit to use composition or >>> inheritance here? >>> >>> For #4 and #5 then its surprising that there is RPC or split/join in the >>> onComplete method. The onComplete method is called with the completed >>> subtask and any exception/error executing onComplete isn't going to change >>> the subtask status. Is there a reason you've chosen to put that code there >>> rather than in the subtasks? (for his API then the question as to "where" >>> to put code is a good discussion as it may not be always obvious whether to >>> code should execute in the subtask, in the Joiner handling subtask >>> completion, or in the main task in the processing after join. >>> >>> -Alan >>> >> >> >