4 ms·
And as most commentors totally miss, this implementation "hiding" (encapsulation) reduces coupling and makes writing tests MUCH easier. If you want to test all
by michaelteter 3y ago
And as most commentors totally miss, this implementation "hiding" (encapsulation) reduces coupling and makes writing tests MUCH easier.
If you want to test all paths on a multi-line function, you have to do a lot of duplication and sometimes a lot more setup for each variation you're trying to test.
But another source of complexity is OOP itself. Mixing code and data is a horrible affliction we have accepted. All we need is some data structure (which can be a rigid class/struct, and then a module of appropriate operations which work on that data structure (or even elements of that structure).
We can have mostly pure functions that are 100% covered by tests... and if we choose appropriate names for these functions it allows us to think in terms of higher level "steps" (each of which we really don't care about how they are implemented).
All that said, we're debating over relatively small differences of approach in Ruby. For a really good time (not), go look at Python codebases. Then some of the people here who are arguing for a single function with 10 lines that just does the full job will find themselves staring at 100 line Python functions and thinking, "we should break this into individual functions" :).
- whstl 3y agoAll methods are private except for the initializer and create_user. Therefore, there's no difference in terms of "implementation hiding (encapsulation)" between the original 4-line function [0] and the class I presented. Also, there is no testability differences, unless you break encapsulation in your tests and test private methods directly with things like send(:find_admin_role). As much as this is seen in Ruby, this is not something that would pass a code review. This class is just 9 methods in a trenchcoat pretending to be that 4-line function. And in case you think this not a strawman example. I provide a real-world case in another message. [2] [1] https://news.ycombinator.com/item?id=38492002 https://news.ycombinator.com/item?id=38492002 [2] https://news.ycombinator.com/item?id=38493809 https://news.ycombinator.com/item?id=38493809
- vidarh 3y agoThis implementation isn't hiding any more details than a single method. In fact, in Ruby it hides less, because bypassing "private" is trivial, and manipulating an object's instance variables is trivial. If you care about encapsulation, that makes this approach worse than a single method. If you need to maintain state across invocations (this example doesn't), then capturing local variables with a closure (lambda/proc) provides far tighter encapsulation than a regular object in Ruby.
- michaelteter 3y agoInformation/implementation hiding isn't for some kind of security or access protection; it's to reduce the cognitive load of understanding a system. By having the main functions before the private, and then having any number of simple or complex private implementation functions, we make it clear to the reader what the contract is. If the functions are well-named, the reader can just read up to the private and stop. They will understand what is going on without having to know any of the "how" details. That's a very good thing. In fact, we well written system using this approach can be read and understood by less technical people (also a good thing, for many reasons). Whether the contents of do_this_specific_thing() are just one line or 100 lines, that point is that we don't have to know or care. We just know that do_this_specific_thing presumably does what its name says, and we accept that and don't worry about HOW. When you inline those tiny functions, as the author argues for, now the reader has to recognize what each implementation line means. Of course we might say it's obvious, but it's less consistent and clear than a series of calls to functions which are well-named and take arguments which are well-named. And as software engineers of any experience, we know that eventually one of those "simple" actions will become more complex due to some new requirements. Then that one line becomes more lines, and then of course it will end up as its own function. Over time, many of those simple steps will become more complex out of necessity, and eventually we'll end up with all those individual private methods (with bigger bodies). As for maintaining state, I personally argue against any OOP style. All of those action functions can just be functions in an appropriately-named module. They should all take input arguments (the data), and ideally not even mutate the input but rather return a value. That value could be a new version of the input data (can be memory/time expensive, so not always the best choice), or they can be just a data structure of changes. I'm not a fan of lambdas, however, as they require more human memory to keep track of what's going on in a system.
- lstamour 3y agoIt’s true that if your goal is something like Cucumber BDD which can be read by non-programmers, then you would want an abstraction layer over the code that hides some of the syntax. This is also where we could get into Ruby being used as a DSL. The only gotcha is that the reason AppleScript is so hard to write while being so easy to read is basically the same reason we use IDEs to write Python and Ruby and generally prefer TypeScript to JavaScript. Namely, the coding assistance. And let’s not touch the maintainer nightmare of having to keep method names up to date with a complete description of what the method does. You might end up with twenty words or needing multiple methods that are no-ops if you ever call a shared library with any amount of complicated functionality. Alternatively, your description of what is done is so high level that it doesn’t actually describe what’s done in a detail sufficient for non-programmers to understand. I guess where I’m going with this is that it’s like comments that describe what the method does. We’ve all been there when the comments don’t accurately reflect reality, because someone, maybe through automation, changed how the system works and didn’t update the comment. It’s hard for me to see as much value in this presentation over an IDE that could auto-inline the function bodies and leave a comment above each with the method name.