Isn’t this a perfect use of an anonymous class with a base class?

> On Aug 17, 2025, at 4:00 PM, David Alayachew <[email protected]> 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 <[email protected] 
> <mailto:[email protected]>> 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

Reply via email to