5 ms·
i really don't see how AlgorithmClass().WithXValue(10.0) .WithYValue(20.0) .WithEtc(...) .Run()
by keymone 7y ago
i really don't see how
AlgorithmClass().WithXValue(10.0)
.WithYValue(20.0)
.WithEtc(...)
.Run();
is any better than
Algorithm(x=10.0, y=20.0, etc=...)
the argument that text books teach us algorithms in form of single function with inputs and outputs is quite weak, there's a very simple solution - break up the large function into smaller functions.
i'll always prefer a set of isolated functions with explicit argument and return declarations over a class implementation with methods because:
- argument list tells me immediately what is required and what are possible additional options as opposed to having to scan the method list for methods matching `WithFooBar` names
- methods become coupled to internal state of the algorithm object because let's face it - code rots, maintainers take shortcuts, state proliferates, everything that is allowed by a compiler/interpreter will eventually make it into the codebase; it's much more preferable to avoid/diminish most of these problems by writing isolated functions
- explicit arg/return declarations help with default static analysis/checks, much harder to do that with classes and internal state
- keymone 7y agoalso, a mention of GoF Command pattern further convinces me that all design patterns are just workaround to shortcomings of mainstream languages of the time and are simply unnecessary in proper FP languages or at least languages with higher order functions and other functional features. cargo culting design patterns into languages that don't need them is ironic.
- TheOtherHobbes 7y agoDesign patterns often seem to rearrange complexity instead of removing it. If you split everything up into nice manageable <40 LOC chunks, you now have tens or hundreds of nice manageable <40 LOC chunks to document, use, and remember - probably spread across a similarly large number of files. It looks neater while you're writing it, but it's debatable if it's any easier to understand if you're trying to debug it. Especially if you're coming in cold, and there's an unnecessarily high level of abstraction, added for the sake of following the patterns. See also: https://ieeexplore.ieee.org/document/4493325 https://ieeexplore.ieee.org/document/4493325 Realistically, the benefits don't seem to be empirically proven - certainly not with any persuasively high level of independent replication.
- andyg_blog 7y agoOne reason to break up a function is deep logic nesting. If you consider the Code Coverage metric a valid metric, then it says we'd need 2^N independent tests to validate each possible branch of logic in a function. Breaking that nesting can reduce the number of required tests by an order of magnitude. At that point we may need an integration test to validate that we can transition from one to the other neatly, but this grows linearly instead of exponentially. Deep nesting often comes with very long functions.
- Sean1708 7y agoHow do objects removing branching from an algorithm? It may not be there as an if-statement, but (unless I'm missing something) you'll still conceptually have a branch.
- andyg_blog 7y agoIt's not objects necessarily; they're simply a way to group a set of related functions. It's functions. Separate functions can be separately unit tested. Then you can write an integration test that uses each of those functions. The testing obligations can be overall much smaller as a result.
- agumonkey 7y agoBecause it is not. It's oop distorsion field at its finest. You can even find talks about why not do that (pyconf iirc). Basically it's useless réification of functions as you state. But when you love oop (and probably didnt use other things) well it's the ultimate itch to scratch.
- seanmcdirmid 7y agoThat really is a straw man. There are plenty of OO enthusiasts who would immediately call out fluent APIs as being very bad style. Ideological demonization is pointless for advocates of either side.
- agumonkey 7y agoI'm sorry, I've been burned too badly by the OO world (not helped by the fact that I went in college at peak J2EE). And to a certain extent.. I think this crowd still has issues. I spent time on #java recently and I felt this acid confidence in OOP that stimulate their mind that I really cannot fathom. I used to be like that, until I left this field. Now this is just personally, but of all the things I've read about, it was the less useful for my brain. SQL did teach me some, pre OO Ada did teach me some, ML taught me loads, Lisp worlds, forth etc etc yet OO falls flat to my eyes 99% of the time. To be honest, when I read 'algorithms as objects' I upvoted, thinking it would be distributed logic as object ala smalltalk. But again, fluent lipid layers.. And I think, the paradigm is to blame. Now IIRC, you have a lot more knowledge about PL than most, so maybe you see OO in a different light, a more rounded way.
- seanmcdirmid 7y agoI’ve been burned by FP ideologues as well, I really don’t see a difference between junior developers cargo culting X vs cargo culting Y.
- Sharlin 7y agoThe builder pattern is a reasonable workaround if your language does not support named and/or default parameters. It’s quite idiomatic in Rust, for example. But the builder should usually be an entity distinct from the result.
- andyg_blog 7y agoI agree with x=10.0 etc, but not all languages have named arguments :-) This is but one way to simulate them. Note that Fowler was using what appears to be a Java factory. Breaking up the large function into smaller functions is ultimately the goal, yes. In my experience, researchers without software engineering backgrounds are very against breaking up an algorithm this way. For the most part, they think that it makes it less readable, because now their algorithm isn't in a single block like it is in the textbook. (The other argument they often give is that they fear breaking it up will impact performance). It's certainly possible to use structured programming here and instead encapsulate the state into a little struct that is passed around; this was the way things were done in C before C++ came along and created classes, and there will always be people who think this to be the superior approach :-) A set of isolated functions can get hairy if you're not able to hide the helpers away from the entry points; not all functions are necessarily generic and reusable elsewhere (if you end up with it that way for your specific application, great!). Encapsulation into a class or namespace can help with that. I'm not sure I agree that isolated functions protects us against code rot more so than with classes. There is always refactoring to consider in both cases. In one camp, you have immutable data classes with functions operating on those data classes and possibly returning another data class (functional programming). In another camp, you encapsulate a reasonable amount of behavior into a class whose methods may mutate the class data (OOP). Both have their strengths.