7 ms·
you can't realistically develop useful software in pure-procedural way without introducing a lot of global state. Even if you look at well-designed non-OO code
by btilly 7y ago
you can't realistically develop useful software in pure-procedural way without introducing a lot of global state. Even if you look at well-designed non-OO code like, say the Linux kernel, you'll see that there are object oriented concepts like information hiding and polymorphism all over the place; they just don't formalize them with the "class" or "private" keywords.
You are misattributing that to OO programming.
The original edition of Code Complete, written in 1993, makes absolutely no mention of OO programming or any OO principles. However it has a very good discussion of information hiding, and procedural code written in the various styles that it recommends do not create a lot of global state. (And yes, I have worked with such code.)
One of the best things about OO was that it pushed programmers who had not absorbed best practices towards that. However OO encourages combining the principle of information hiding with OO notions of inheritance. Many learned the hard way to prefer composition over inheritance. (Many, unfortunately, never learned that. Just as many procedural programmers never learned about information hiding.)
- arc776 7y agoI feel like inheritance is the actual problem with OOP. Trees of types with increasing specialisations just don't describe many real world problems that well. When you do build programs like this, code gets more and more rigid until it becomes a maintenance nightmare, and extremely expensive to pivot functionality. Sometimes having a complex object hierarchy literally stops you making some new feature. A lot of mental effort is sucked up in mashing problem spaces into hierarchies, and once there the program is constrained by them.
- zozbot234 7y agoIt has little to do with subtyping per se and everything with the way that implementation inheritance, specifically, breaks modularity. The whole idea of inheritance contra composition is that every call to a virtual method-- including calls internal to the class-- goes through a dispatch step so that the actual code that gets executed depends on whether the object is one of a "derived" class, where that method might have been changed in ways that might break any amount of expected invariants. This introduces brittleness both in base class code (which generally has to call methods that might get overridden in derived classes) and in the derived class itself (which has no way of knowing what invariants might be expected of it as part of base class code). Implementation inheritance is often justified as a way of "reusing" code, but as it turns out, we've merely introduced undesired coupling instead of the seamless reuse we might have expected.
- dragonwriter 7y ago> the derived class itself (which has no way of knowing what invariants might be expected of it as part of base class code). In principle, with a sufficiently-robust type system, the invariants expected by the base class would be encoded in the typing, and the derived class would only be able to narrow them. (Of course, OOP languages tend to either be untyped or have insufficiently robust type systems for this; robust type systems are more frequently associated with functional languages.)
- temac 7y agoIf the type system already trivially enforces your custom invariants, aren't the types defined by your classes already in it?
- zokier 7y ago> every call to a virtual method-- including calls internal to the class-- goes through a dispatch step why would you make internal methods virtual though?
- bregma 7y agoVirtual methods should only be internal methods. There's no point in making the public API have virtual methods. That would be a bass-ackwards parody of proper OO design.
- kragen 6y agoIt's common to put virtual methods in a public API; indeed, in Java we have the "interface" construct consisting entirely of public virtual methods and, occasionally, named constants. (In more recent versions it can also include default implementations.) In Golang the only virtual-method mechanism works by means of a similar interface construct, which also only contains public virtual methods. So, for example, Java's Map is an interface type with, for example, .get() and .put() methods; TreeMap and HashMap implement those methods differently. There are similar things in Smalltalk-80 and the C++ STL. So clearly you think Java, Smalltalk, and C++ are "a bass-ackward parody of proper OO design." I'm interested to hear what systems you consider paragons of proper OO design.
- btilly 7y agoI feel like inheritance is the actual problem with OOP. Trees of types with increasing specialisations just don't describe many real world problems that well. I firmly agree. I wrote https://www.perlmonks.org/?node_id=318257 https://www.perlmonks.org/?node_id=318257 over 15 years ago. I still agree with the fundamental criticism of "OO everywhere" that I offered there.
- GlennS 7y agoThanks, enjoyed this. That SICP quote really nails it too.
- jholman 6y agoThank you for the excellent piece. Though w.r.t. the speculation in your final "Disclaimer", I'd expect the algebraically inclined to prefer either the elegant combinatorical explosion of a J, or the build-it-yourself language of a metaprogrammed Lisp.
- chrstphrhrt 7y agoAgree 110%, and I want to take your notion as a cue to expound a little more from personal trauma :) I find OOP useful for configuration and library-level APIs (e.g. for injecting dependencies and wrapping things), when there is really zero expectation that a user should have to understand the class definition entirely. But that's about it. Where it seems to break down for me is when classes are actively used at the application implementation level for general encapsulation. E.g. data access layers. This can be done perfectly fine with modules/packages. Adding inheritance is just asking for trouble in a team setting IMHO. Trouble comes when there is no clear peer review and style policy to avoid classes for anything other than config or distributing libraries (as separate projects). What ends up happening is a proliferation of subclasses or method overrides when developers are in a rush to ship features without understanding the whole codebase. This is a technical loan with very high interest. It makes sense rationally in the moment, as classes have an inviting feel to the user as a kind of grab bag of related functionalities that are easily introspected (at first). Compare it to searching through docs for all the different namespaces in a package and learning what types their various functions support, it requires more thought and grasping the concepts of the library. Alternatively, a class with a broadly defined purpose starts to look like a common utility to throw things at. It's a grab bag of stuff that is more amenable to hacking with blinders on, in a way. Next thing you know the chains of inheritance and method overrides have grown into a very hard to disentangle hydra. The accumulated overhead of maintaining it is compounded by the debugging challenge of knowing where exactly a given instance is coming from and which layers it has been through on its way to a given breakpoint.
- xt00 7y agoThe concept of "if you want to touch this code base you have to understand the entire thing" is pretty tough on basically everybody but the people who either wrote most of the code or who live in that code base all the time. It would seem like code-bases should (either because people architected it to be possible to work with without understanding the whole thing or the programming language helps facilitate it) be able to be worked with while only understanding a portion of the code base. In some cases classes and interfaces help with that, but in plenty of cases you end up in one of the sort of child classes trying to figure out how to get out of the hole you are in where you can't interact with anything anywhere in the code base and you have to basically plumb pipes through lots of places to allow moving data where you want.
- GlennS 7y agoAgree completely, that's a really nice summary of the key problems. The mental effort of building up taxonomies in particular is exactly what put me off inheritance (and fussy type systems in general). It's too easy to jump down that rabbit hole without considering whether it's a productive activity.
- im3w1l 7y agoThis is highly codebase dependant. I have seen code with crazy hierarchies like you say, and ones without it. It's possible to "just don't do that".
- aryehof 6y agoPerhaps an issue is that the well known saying "prefer object composition over class inheritance" isn't done, because a majority of programmers simply don't know how to apply it. Inheritance is used inappropriately (outside of frameworks) almost everywhere.
- deleted 6y ago[deleted]
- seanmcdirmid 6y agoPolymorphism via indirect virtual dispatch basically means you want to treat different kinds of objects differently via the same interface. If you take OOP as programming with objects, it most definitely is doing OOP; if you take OOP as being Java or some other more specific definition, then it isn’t. When you implement an extended jump table (a table set that extends an existing table def) in C, that is also just inheritance. There is a good reason the first C++ compiler was just implemented as a bunch of macros in C.
- deleted 6y ago[deleted]
- tcgv 6y agoThe concept of information hiding came a bit before that, almost 50 years ago, first being described in a paper by David L. Parnas entitled “On the Criteria to Be Used in Decomposing Systems into Modules”. I wrote a blog post earlier this year based on this paper, describing how using the information hiding criterion naturally leads to an improved system structure when compared to using a procedural criterion [1] I'm an advocate for OOP because it greatly favors information hiding and encapsulation, helping to manage the ever-growing complexity of software projects, making "it possible to develop much larger programs than before, maybe 10x larger" (quoted from the linked article) [1] https://thomasvilhena.com/2020/03/a-strategy-for-effective-system-modularization https://thomasvilhena.com/2020/03/a-strategy-for-effective-s...
- rramadass 6y agoThank you. Every Software Engineer should read Software Fundamentals: Collected Papers by David L.Parnas. People are nitpicking things and just parroting what is written in other books without really understanding the idea behind it all. For example what is a Interface/Class/Library/Framework/Module(logical and physical) if not Information Hiding at different levels? The concept is the same but the realization is different. OOD/OOP just gives you convenient syntactic sugar to express them. The rest is up to you.