Two OO ways to design something, curious what everyone's thoughts are? This is w/out context of a framework, just purely OO perspective.

Option 1 - Stateless object w/ Rich Result

Option 2 - Stateful object (e.g. command pattern + internal state to store results)

https://gist.github.com/davetron5000/19aa850df641be6c334e9b64a944b6c8

My thoughts follow, but I am not sure which is the "best" pattern - again all things being equal/not in Rails/etc.

stateful.rb

GitHub Gist: instantly share code, notes, and snippets.

Gist

Stateless code seems easier to test and operate, since it's basically just a function. Certainly, it can affect the world around it (e.g. in a database), but it's a long-lived object that performs a function, and this is pretty simple.

The downside is that the results of the action require a separate object to describe, requiring either an inner class to help manage or coupling between two classes.

This can also lead to architecture astronautics if you try to generalize the result class.

Stateful, on the other hand, keeps all concepts together: the operation and its possible results are all in one class, which feels cohesive and easier to manage/evolve the code.

The downside is that the object, once invoked, cannot/should not be invoked again. Thus, you'd probably need some safeguards to help avoid that.

The stateful approach also could be awkward if you wanted multiple "doit" methods. You then get a convoluted object with lots of optional values for results.

The "lazy mode" of these approaches is woerth considering:

lazy devs applying the stateless approach will use boolean return value, which suck

lazy devs using the stateful approach will overload existing objects with more methods.

I believe that the way in which a design decision plays out under stress is important.

Of note, this is why OO books like POODR leave me wanting. The examples are so trivial and unlike anything resembling real code that they fail to give true insight.

You can retcon any set of objects to any domain - this is not helpful at coming up with a good design. And, to restate, "good" means more than just "at rest is it understandable" but "will devs easily adhere to this design under stress”.

@davetron5000 Great point about stress. I think I prefer stateful, because in my work context it seems better known. Even if the stateful class grows in a bad way it's easier to change, I think. So I imagine people have an easier time with it once it exists. In short: stateful "communicates" better, I think.
@davetron5000 I've been liking the "interactor" gem, which codifies the stateless approach. Biggest benefit is reducing cognitive load by establishing conventions for this problem
@m1foley yeah that’s a bit too DSL and abstracted for me.

@davetron5000 I seldom think about this? My go-to (heh) is generally something like:

def self.call(**kwargs)
new(**kwargs).call
end

and then #call can return whatever it likes

@geeksam That's a lot of code to avoid calling new - unless I'm missing something?

But the question is more "what should call return?” and "should the class have internal state that is inspected to understand what call did”

@davetron5000 I usually deploy that pattern for things that need to act like a "static function" on the outside but that have complex/stateful internals. (Feel free to read this as "I spam Extract/Inline Method and change my mind a lot", lol)
@davetron5000 actually, that is kind of an answer to your question: I will go to at least some lengths to avoid having to reset state on an object, preferring instead to throw the whole thing away and construct a new one.
@geeksam @davetron5000 so much nicer with 3.1 arg forwarding too!
@davetron5000 result class is already generalized and called Result Monad
@davetron5000 stateless are...objectively better, lack of mutable state gives you simpler reusability and less code paths that can go wrong 🙂
@solnic You are not worried about coupling between "doit" objects and "result" objects? Or the extra effort needed to create structured result classes separate from the "doit" class?
@davetron5000 I am not, I'm been doing this for a decade+. Never looked back. Mutability sucks.
@davetron5000 oh and I don't create result classes, I just use plain data or stuff from dry-monads

@solnic How do you capture why an operation could not be completed in order to advise the user of the problem and how to fix it? I find "validations attached to a record" insufficient in a lot of cases.

For example, draft blog post -> validate title, publish blog post -> validate title + body & require approvals, e.g.

@davetron5000 dedicated validation contracts, every use case may have its own contract, validation happens before anything else happens
@solnic But not all checks for “can this be called" can be fit into data validation, right? Often you have to consult external services/data stores to figure out if an operation can be performed and, if not, provide the user an explanation.
@davetron5000 yes I do that in contracts

@solnic It sounds like a "contract" has logic for “can the operation be invoked?” as well as some structured data to describe all possible things that can go wrong, such that the user can be told what went wrong.

And then a separate (stateless) "doit" class to perform the operation, that is coded under the assumption that the contract has been checked first?

Thus, the contract and the "doit" are coupled, despite being separate classes?

[just trying to understand how it would all work]

@davetron5000 @solnic just return Failure monad with all the details
@jandudulski @solnic the structure of the errors is use case specific however. So it cannot be generalized beyond “yes it’s an object with attributes” but Monad lays no claim to that.
@davetron5000 @solnic true, but it doesnt differ much from designing api of stateful approach - you still have to agree on a format, codes, messages, methods/field names etc

@davetron5000 we tend to move validation to where end user input is received, and we also tend to avoid result objects unless there truly are multiple outcomes inherent to what the thing does. We don’t use result object states to model malfunctions, for instance. This simplifies the decision.

This is a circumstance where the more correct approach will vary case by case, in my experience. But the reusable approach tends to be more common in practice for us.

@ntl What do you mean "reusable approach”?
@davetron5000 the one whose do_it method accepts input arguments can be reused indefinitely (in contrast to the one that accepts values in its initializer)

@davetron5000 stateless is nice and clean but inevitably requirements evolve and it no longer need to do “just one thing” or doing the one thing involves a series of internal methods and having internal state would clean up their parameters.

I think “best” probably changes over time (usually evolving from stateless to stateful)

@jamie @davetron5000 stateless can tip over into swivel-eyed puritanism all too easily, and nobody wants that.
@jamie So you prefer to use instance variables to pass data between private methods (vs using method arguments)?
@davetron5000 @jamie Don't answer, Jamie! It's a trap!
@emma @jamie lol, I would have a follow up question but am honestly looking for experience, not to shame anyone Not Doing It My Way™
@davetron5000 very often, there are 1-2 objects that most internal methods need. It bugs the shit out of me to pass them around.
@jamie This makes sense to me, at least for objects that are dependent e.g. a service that the class calls. I would not do this for stuff the class operates on or for cases of functional decomposition to private methods - those would take args for what would've been local variables (hard to explain in a toot)
@jamie @davetron5000 when complexity grows, you extract things into new objects and use them as collaborators; internal state doesn't clean up anything, it just makes things hidden and less obvious, it's the OO lie so many people are used to
@solnic @jamie @davetron5000 but you can also end up with a hundred tiny, neat -- and completely unnavigable classes. so there be dragons on that approach too
@emma @jamie @davetron5000 not in my experience, functionality shouldn't be split in a too granular way, you extract things only when something is hard to understand. Lack of state make things easier to extract into smaller pieces too. It's also easier to reorganize and move things around when you don't have state hidden in a bunch of objects.
@solnic @jamie @davetron5000 but you know that my experience has been otherwise, cos I just said so. so it's something that can happen if someone's sense of whether something is hard to understand and therefore extractable differs from someone else's.
@emma @jamie @davetron5000 premature extraction and/or too granular extraction is a problem of its own though. It's not like stateful objects will shield you from it :) I'd even say they make things worse as what may happen is that lots of objects will be coupled to a lot of state without a good reason
@solnic @jamie @davetron5000 "let's not prematurely abstract" is my mantra at work!

@davetron5000 The answer really is “it depends” 😅

I usually always try to answer these 2 questions first:
1. What's the abstraction? What I actually try to encapsulate and simplify?
2. What would be the best API for that abstraction?

If the abstraction is just a command: do the thing -> report the result, then yeah the 1st option is better imo. But if the abstraction should actually disallow multiple calls, then this option will simply not work. That state has to be somewhere.

@davetron5000 We're assuming in the first version that the "some logic" is stateless or idempotent?

I find that more developers are likely to understand the stateful version, and while I may decry the lack of genuine OO design skills, that's still a factor.

I've basically written the stateful version a lot but I do like the stateless version where it's feasible.

@davetron5000 In most of these cases that I write, the object isn't reused, so calling it multiple times is rarely a problem in practice.
@noelrap yeah the core logic’s only “state” is to leave behind a structured explanation of what it did when called.
@davetron5000 I think generically it's better for the class to return a value rather than own the value as state, but I've written the "own the value" enough times that I clearly don't think it's that much of a win in practice.
@davetron5000 The second version is something that I sometimes call “syntactic" OO -- it's a very generic pattern that works in almost any domain, so if you are a consultant going from project to project, it's a very easy way to get a plausible system design quickly. (I guess that's true of both of these, but the custom result object makes me think there's more domain design going on in the first case)
@davetron5000 In a language like Ruby where static analysis and ensuring immutability are difficult at best, the stateless model provides a lot of advantages. The result object can’t mutate the rest of the system, there’s exactly one place where it’s produced, etc.

@davetron5000 I’ve built a bit of a system for constructing API services (services that interact with an external API), structured like the “stateful” version. Not sure that I’d say it’s “better,” but it was at least fairly straightforward to build.

Fully agree that the stateless would be cleaner to test. I’d probably give that a shot in the future if this sort of problem needs solving for us again.

@davetron5000 notably, those services all have a single “perform” method and no other method actually executes anything. The rest are all various getters. And “perform” also returns something useful (different depending on what the service is going).

@davetron5000 I really dislike the command pattern, at least in the ways I’ve seen it used (in rails).

Normally there’s some parameters to the initialize method, then a method to call in order to do the work (which might also take parameters). And then if the state is used to track the results, presumably there’s a third to retrieve the results.

All the usages of it I’ve seen could be replaced by a single method that takes parameters and returns a result (perhaps a rich one).

@davetron5000 I prefer something closer to number one, except I use a module with a .call method for the “verb” and nest any classes I need within the module scope. So your example would return a DoSomething::Result.
@davetron5000 I think of `do_it` as a special case constructor for a `DoItResult` object. I’d put the stateless one into one class, with the do_it code in a class method. Then you get both value objects and cohesiveness. If ever there is an alternate operation to make a DoItResult, make a new class method constructor.

@davetron5000

An “issue” with the second one in this example is that the interface becomes “temporal” — you *always* have to call `do_it` before you can access the results.

In these cases I prefer te return some result object. It juist suits the single-purposeness of this type of service/command object better.

That being said… I use ActiveRecord everyday and it has that same temporal interface.

It feels like less of a problem there.. Entities/Records aren’t stateless. Commands should be?