5 ms·
Indeed. The problem is trying to apply the principles of "Clean Code" or more generally of OOP everywhere. There are surely cases where having an interface as a
by alerighi 2mo ago
Indeed. The problem is trying to apply the principles of "Clean Code" or more generally of OOP everywhere. There are surely cases where having an interface as an abstraction and multiple implementations makes sense. There are case where it doesn't, and if you take as a dogma "everything shall be the implementation of an interface" you get more complex code and performance penalty for nothing.
There is nothing wrong with having procedural code with a switch case, as there is nothing wrong in having global variables, in having even goto, depends on how you use it.
- tcfhgj 2mo agoThe principle of clean code includes KISS, therefore complex code for nothing isn't clean code
- weegee101 2mo agoSure, but then Clean Code goes and directly pushes for polymorphism in an area (branching) where indirection and polymorphism is known to be costly for both complexity and performance. An aspect of this that I wish Muratori had touched on when he wrote this in 2023 is how each of these tenants he has issues with in Clean Code are just trading complexity. All four of the structural rules that Muratori demonstrated issues with generally don't reduce complexity. At best, each trades one type of complexity for another. There are some great ideas in Clean Code, but outside of DRY, the structural recommendations tend to be more harmful than good.
- philippta 2mo ago> There are surely cases where having an interface as an abstraction and multiple implementations makes sense. I think most people aren't aware of the alternative, which is: A function that can call different implementations based on some other variable. E.g. instead of having RealDB and MockDB type have a createUser() (method), you have a createUser() (function) that switches part of it's logic based on what DB is selected. That's the prodecural way of achieving the same thing without needing a concept for virtual functions. Casey explains this in the long discussion with Uncle Bob.
- aidos 2mo agoAre you suggesting something like this? def createUesr(db): if db is type1: behaviour1 if db is type2: behaviour2
- philippta 2mo agoSee Casey's code snippets here: https://github.com/unclebob/cmuratori-discussion/blob/main/cleancodeqa-2.md https://github.com/unclebob/cmuratori-discussion/blob/main/c...
- TheCoelacanth 2mo agoYes, but there's not a chance that there's a material difference in performance between those options because of virtual functions. Unless you're doing something really stupid, nothing other than the DB access is going to be worth optimizing. If those two options are accessing the DB in exactly the same way, then they will probably be within 1% of each other in performance.
- alerighi 2mo agoDepends, for example if the variable that selects between the two implementations is a compile time constant (#define or constexpr variable) the compiler can really remove the conditional and all the code of the choice that is not always selected leading to higher performance and smaller footprint.
- jdlshore 2mo agoIt doesn’t matter. You’re saving nanoseconds when a db access costs milliseconds. Better to focus on making the code easy to understand and change and focus your effort on optimizing the database.
- zeratax 2mo agoyou are hyperfocusing on a single example. there are contexts where it matters and others where it doesnt sure, but i dont think either approach is inherently easier to understand or maintain
- calvinmorrison 2mo ago> There are surely cases where having an interface as an abstraction and multiple implementations makes sense. A tried and true way solve problems is by adding more layers of indirection, starting with an interface makes it trivial to swap things out. I just did a rewrite of some old sound tool that was hard coded to OSS and Alsa. Now i wanted Pulse and Pipewire, this ended up requiring basically a rewrite because there was a lack of a good interface and assumptions everywhere. Instead now I have some good interfaces and adding whatever the next Linux audio stack comes in - it likely won't be a problem.
- josephg 2mo agoI think about this like hinges in a door. For a door to move, it needs some hinges. Otherwise the door can’t move. But dont get carried away thinking more hinges is always better. We don’t cut door panels in half and reattach the pieces together with more hinges. That would make the door complex and weak. Like a door, your software should have hinges (interfaces) in the places it needs to be able to change. And it shouldn’t have hinges in places where it won’t change. Rigidity allows for simpler code and better performance. Flexibility allows for changing requirements and modularity. The mark of an experienced software engineer is having the judgement to know ahead of time where your code should be flexible and where it should be rigid. A good rule of thumb is to only add an interface when you have 2 or more implementations you want to code up. Until then, just call methods directly. If you don’t have 2 different case studies, you’re going to design the API badly because you don’t know the real requirements.
- calvinmorrison 2mo agomaybe I am using interfaces the word differently. I mean interfaces as in a language construct, a library, or some programming mechanism to separate things out. Even if I only have -one backend- of something, it's still often a good idea to separate those concerns from the rest of your program, stack, etc, just to make things reason about. to have a mental boundary about where things are happening, or to debug, etc.
- inigyou 2mo ago