OO Considered Harmful - Phil Nash - CppCon 2020

แชร์
ฝัง
  • เผยแพร่เมื่อ 31 พ.ค. 2024
  • cppcon.org/
    github.com/CppCon/CppCon2020
    ---
    Is C++ on OO language? What does it mean to be an OO language anyway? What were the original goals, and how have they turned out?
    What other approaches are there? Some say there was nothing wrong with Structured Programming and we should go back to that. Others push for a more Functional approach. There's also Logic Programming and others. Is there one right answer? Does it depend? Does it blend?
    Experienced C++ programmers think of C++ as a Multi-Paradigm language, capable of moving between paradigms as needed. But is it particularly good at any particular one? Is that focus shifting? Where is it going, and why? What can we learn from other languages.
    If all this sounds very abstract, be assured that we will look at real code and techniques you can apply today or, in some cases, the near future - as well as a glimpse of what may be down the road. More importantly we try to put it in a context that helps answer "why?"
    Along the way we'll take a tour through C++'s approach to OO, Generic Programming and Functional Programming.
    ---
    Phil Nash is a Developer Advocate at JetBrains, author of Catch/Catch2, co-host of cpp.chat, host of C++ London, chair and organiser of C++ on Sea.
    ---
    Streamed & Edited by Digital Medium Ltd - events.digital-medium.co.uk
    events@digital-medium.co.uk
    *-----*
    Register Now For CppCon 2022: cppcon.org/registration/
    *-----*
  • วิทยาศาสตร์และเทคโนโลยี

ความคิดเห็น • 61

  • @kapa7197
    @kapa7197 ปีที่แล้ว +12

    OOP is not harmful. The problem is users trying to force idiomatic OOP onto every design issue even when not necessary. OOP is a tool that needs to be used selectively and with care, because of course it doesn't fit every problem. Any and all other design/model/pattern is harmful too when used indiscriminately.

    • @churchianity
      @churchianity 10 หลายเดือนก่อน

      Meth is not harmful. The problem is users trying to force idiomatic meth consumption into the everyday lives of people, when it is in reality a drug that needs to be used selectively and with care. Any and all other consumable substances including food are harmful too when used indiscriminately.

    • @Peregringlk
      @Peregringlk 9 หลายเดือนก่อน

      Because most OO-users just follow memorized rules as learnt in the university. It's that people are obsessed with OO, is that people does not know any other thing. People don't like to think too much, in general.

  • @berylliosis5250
    @berylliosis5250 2 ปีที่แล้ว +12

    Neat. I was anticipating this talk joining the legions of "talks that complain about OOP without actually addressing the issues with OOP". This one gets pretty close!
    My issues are:
    - Inheritance is (almost) completely useless, and incredibly problematic as a language feature (it's my least favorite way to get polymorphism; any one of the alternatives provided are much better)
    - Encapsulation is useful, but is largely unrelated to objects (a good module system does the same thing... but more expressively and flexibly)
    - With those out of the way, there are no benefits to thinking about things in terms of objects rather than systems or modules or components or whatever (which exist in every programming language). This frees up types to be used treated as _types_ rather than the muddled concept of a "class"
    I do disagree with the conclusion - I like something that's in some ways the exact opposite, where mutable state is allowed locally (within a function, or anywhere "nobody else can see"), but as little shared state (between threads, yes, but also between different parts of the code) as possible is mutable (i.e. usually zero). But that's a different topic, and I broadly agree with the points of the video.

    • @freedom_aint_free
      @freedom_aint_free ปีที่แล้ว +1

      After a long time of imperative programming (I started programming in 8 bit BASIC in the 80's as many have done so...), I was finally introduced to functional programming concepts by Python 3.x, and I was amazed how better than Python's OOP they were at least for me (I often program number crunching heavy applications in the STEM fields) and it was my gateway drug to Functional Languages: Lisp, F#...and then, I went to Jesus so to speak: Haskell.
      To me Haskell put the last nail in the OOP's coffin. I get that it's not for everybody, as I feel that in order to really conjure all of its might one has to have a good mathematic foundation, but it can be a sweet gateway drug to higher mathematics too!

  • @possible-realities
    @possible-realities 3 ปีที่แล้ว +33

    Interesting talk, but it's still not quite clear to me which part of OO that you consider harmful (and where that leaves us). Mutability, I think? (minimize it) Inheritance? Virtual dispatch? Others?

    • @ashw6015
      @ashw6015 3 ปีที่แล้ว +12

      I thought the same thing. And it's surprising to me to see so many people still miss the point of OO. The audience does, based on those votes and I'm not too sure if Phil gets it either. Features like encapsulation and inheritance are mechanisms of OO but they are not the essence of it. A key value of OO, and some would say the primary value, is the ability to manage dependencies. As an author of a test framework, who presumably understands the horrendous state of testing in C++ when compared to other areas of our industry, I'm surprised to see that the high value of dynamic polymorphism wasn't directly addressed in the context of testability, build efficiency, independent deployability and the other benefits you get from a well managed dependency graph. We all know that dynamic polymorphism usually incurs a performance cost over static polymorphism, but it also comes with a huge reward. One that it feels like half of the C++ community doesn't understand and which the other half purposefully give up for the sake of performance. But neither of those groups would be wise to outright deny the advantages of well designed seams implemented by dynamic polymorphism as the default, only ever overridden with static polymorphism when performance truly demands it.

    • @darkengine5931
      @darkengine5931 3 ปีที่แล้ว +8

      I cannot speak for Phil but like him, I have moved further towards a functional style over the years as with the case of John Carmack. I think all of us would list shared, mutable state among the top of the list of things that made the previous object-oriented codebases in the past so difficult to maintain and reason about. Quoting John Carmack:
      >> Class methods that can’t be const are not pure by definition, because they mutate some or all of the potentially large set of state in the object. They are not thread safe, and the ability to incrementally poke and prod objects into unexpected states is indeed a significant source of bugs.
      [...] Const object methods can still be technically pure if you don’t count the implicit const this pointer against them, but many object are large enough to constitute a sort of global state all their own, blunting some of the clarity benefits of pure functions. Constructors can be pure functions, and generally should strive to be - they take arguments and return an object. [...] A large fraction of the flaws in software development are due to programmers not fully understanding all the possible states their code may execute in. In a multithreaded environment, the lack of understanding and the resulting problems are greatly amplified, almost to the point of panic if you are paying attention. Programming in a functional style makes the state presented to your code explicit, which makes it much easier to reason about, and, in a completely pure system, makes thread race conditions impossible.
      -- John Carmack
      Specifically it's the combination of sharing and mutability of state (or objects) which poses difficulties in very complex and large-scale projects, especially if they're very multithreaded. If the objects are immutable, then the problem goes away. If the objects are not shared and only used in one central place and thread, the problem also goes away. It's specifically when they are both mutable and shared where things get very hairy.
      One telltale sign if your codebase is complex in this way is if you assign equal or greater value to your integration tests in catching problems over unit tests. In that case, the code is likely prone to human error not in terms of implementing individual, atomic, concrete objects correctly but in using them in combination correctly. Also if thread-safety is something you struggle with and have to think very hard about all the time, then that's another telltale sign.

    • @darkengine5931
      @darkengine5931 3 ปีที่แล้ว +4

      Multithreading exacerbates this problem of shared, mutable state (or shared, mutable objects). Multithreading safely and efficiently forces us to think carefully about data access patterns, but data is considered an implementation detail that we shouldn't have to think about so much in object-oriented design... yet multithreading forces us to think about it.
      You could design three separate mutable objects/interfaces which are all individually thread-safe for all of their methods. You can unit test them individually and do a serial integration test and they might function just fine and your tests might all pass. However, the moment you multithread their usage, you might still run into deadlocks if they are not used in a consistent order (inconsistent locking/unlocking patterns of multiple mutexes, e.g.), and maybe only once every blue moon with an obscure bug that's nightmarishly difficult to reproduce.
      So multithreading tends to make us want to think of our objects more like white boxes instead of black boxes, and become more aware of what object-oriented design and encapsulation tends want to treat as hidden/private implementation details rather than foreground design concerns. And that's a conflict that I can't see OOP ever resolving as 8+ core machines start to become commonplace and the consumer demand for parallelism increases unless we start heavily favoring immutable objects.
      It's primarily due to multithreading consumer demands that I think data-oriented design (which tends to be more procedural or functional than object-oriented) and functional design are becoming increasingly popular alternatives to OOP. Functional programming eliminates the difficulties achieving thread-safety by largely eliminating shared, mutable state outright. Data-oriented design mitigates much of the difficulty by bundling data according to access patterns and making data at the forefront of the design (often by breaking encapsulation) rather than an implementation detail which greatly simplifies thread safety even with mutable, shared state.

    • @kuhluhOG
      @kuhluhOG 2 ปีที่แล้ว

      @@ashw6015 his key point seems (to me) to be that non static systems are hard to reason about
      one of the things which make this harder is mutability, but another one is imo also dynamic polymorphism
      don't forget, you may be able to design and reason about a very complex system with a lot of shared mutable data (even if it's made thread-safe and completely unit tested (and you think you have tested all edge cases)), but what is with the other 99% who aren't capable of doing this?
      same goes for goto; there are people who can use it very beautiful and in a way where it's still easy to reason about, but there are RARE

    • @papasmurf9146
      @papasmurf9146 2 ปีที่แล้ว +1

      @@kuhluhOG Isn't that ultimately the problem: the developer? It doesn't matter what your development paradigm is, the problem is the developer in the cube one over from yours. Ultimately, developers have different capability levels. The best a language or design approach can do is maximize the level a developer can operate at -- or minimize the amount of damage a developer does to the system.

  • @williamdrum9899
    @williamdrum9899 2 ปีที่แล้ว +2

    41:04 Here's something I don't get: why declare month and day as int when you only need one byte to represent each? Seems wasteful to me. Is it purely for speed over data usage?

  • @skyeplus
    @skyeplus 3 ปีที่แล้ว +16

    That's a good talk, but I was expecting to be more focused on harmful aspects of OOP. My take on this topic is when faced with real world problems it requires creating a whole wagon of tools to alleviate it's shortcomings. Some issues fundamentally couldn't be resolved efficiently, just resolved. It's Gorilla and banana.
    Data races on update and read, life time safety, making composable, reusable, augmentable operations (for undo history, to have the ability to preview changes, making all of this to work in multi-threaded environment, etc).

  • @abcxyz-nd6xh
    @abcxyz-nd6xh 3 ปีที่แล้ว +7

    I was attracted by the beauty of
    the well-organized behaviour of C struct
    (grouping everything functionally-related
    to be acted on together)
    to OOP and hence C++

  • @444haluk
    @444haluk 3 ปีที่แล้ว +15

    I have never been so offended by a C++ talk title. LIKE!

  • @OlaFosheimGrstad
    @OlaFosheimGrstad 3 ปีที่แล้ว +8

    To clear up a misunderstanding. Simula does have encapsulation through the keywords HIDDEN and HIDDEN PROTECTED. Since all activation records are on the heap, all objects are essentially closures and the class body is executed as a coroutine. I believe it can be extended by inheritance through the INNER keyword. This aspect was later made more uniform in the successor language BETA.

    • @philnash3269
      @philnash3269 3 ปีที่แล้ว +4

      Thanks. That's not something that came up in my research - I'll look into that more. Sorry if I was inaccurate there

    • @philnash1121
      @philnash1121 3 ปีที่แล้ว +3

      Thanks for this Ola. I'm sure I replied to this a week ago, but my reply seems to have gone missing, so I'll try again. When I was originally looking into this I'd read in a few places that Simula 67 didn't have encapsulation (in the private/ protected form - whether with those keywords or not). Prompted by your response here I looked again and do see references to protected and hidden, now. However, AFAICS, these were introduced in the TOPS-10 implementation, and later adopted into Simula 87 standard (main source, Wikipedia: @t - so not in the original Simula 67. If you have anything more authoritative (either way) I'd be very keen to hear it (before I give the. talk again!)

  • @MrAbrazildo
    @MrAbrazildo 3 ปีที่แล้ว +8

    43:50, "encapsulation in C++ is actually broken?", because "a class can not access the private stuff of its inheritance". Have you ever heard about the 'friend' keyword?

    • @kuhluhOG
      @kuhluhOG 2 ปีที่แล้ว

      #include
      class A_private {
      int i;
      public:
      A_private(int i) : i{i} {}
      int get() const { return i; }
      };
      struct A_public {
      int i;
      };
      int main()
      {
      A_private a{5};
      ( (A_public*)( (void*)(&a) ) )->i = 6;
      std::cout

  • @phillipsusi1791
    @phillipsusi1791 3 ปีที่แล้ว +16

    Virtual dispatch doesn't require heap allocation; you can call virtual functions on objects on the stack ( or data segment ) just fine.

    • @philnash3269
      @philnash3269 3 ปีที่แล้ว +11

      IIRC I did say it "tends to imply" it, rather than require. If I didn't it was a slip-up, as that's what I had intended. You can certainly use virtual functions with non-heap objects - but at scale that's quite rare and even a bit specialised. And approaches like using fixed buffer optimisations muddy the waters further - which is what I was alluding to when I said you could mix virtual dispatch with type erasure techniques - but didn't have time to open that can of worms.

    • @JamesNortonSound
      @JamesNortonSound 3 ปีที่แล้ว +1

      That is true, but I think he’s referring to the necessity of using pointers for virtual dispatch.

    • @VasuJaganath
      @VasuJaganath 3 ปีที่แล้ว

      That’s what he said, “usually”.

  • @DomainObject
    @DomainObject 2 ปีที่แล้ว +1

    Excellent talk!

  • @Rishit.Chaudhary
    @Rishit.Chaudhary 3 ปีที่แล้ว +3

    Hi,
    I felt that the talk was quite insightful. Especially towards the end, about the points of "inheritance as being unnecessary for polymorphism: and "the idea of immutability at the low level and handling any sort of mutability at a higher level of abstraction". I feel that the talk did a great job, of taking a step back and going over the different features of OO.
    I feel this idea blends quite well, with one of the points I had read in the book titled: "Clean Code". The point made in the book was that operations/functions called within a single function, should all be at the same level-of-abstraction. I feel that by dealing with mutability at a higher level, makes it easier to understand the operation conceptually without worrying too much about the implementation while using it along with other functions at the same level of abstraction makes the code base as a whole, a lot simpler to reason about.
    I have one question though, about a point mentioned towards the end of the talk that I strongly feel, has serious implications in terms of performance.
    You said that, when we add an element to a persistent data structure, we need to create a copy of it and then add the new element to the copy. This operation can be very-very expensive for larger user-defined type.
    I would like to know your comments on this problem and any tips you could share on how to avoid/handle such cases?
    Really insightful talk,
    Thanks

    • @darkengine5931
      @darkengine5931 3 ปีที่แล้ว +2

      Persistent data structures will never outperform simple mutable equivalents for write operations but an optimized version can often get reasonably close like 2-3 times slower (not 10x). Even 2-3x slower writes might sound like a huge overhead but you have to keep in mind that the persistence makes non-destructive editing, undos, exception-safety, and thread-safety come free of charge. Especially the thread safety can more than alleviate the cost.
      For example, take game engines. Most game engines do not use persistent data structures and do not really multithread much code because they required so much shared, mutable state. So the few game engines that bother to multithread systems tend to multithread rendering away from everything else so that the renderer can be rendering the current frame while the physics engine is working on the new one. To achieve this bare-minimal multithreading without PDS and without race conditions, the game engines that do this use a double-buffered "copy-on-read" strategy... that is to say, they copy the *entire game state* every single frame for the renderer before rendering and thus require double the memory use! If they wanted to multithread the physics as well, they'd have to copy the entire game state twice per frame and require triple the memory use and so forth.
      The PDS doesn't suffer this problem because it uses copy-on-write and only copies the few parts of the data structure which are modified to produce a new version of the structure without mutating the previous. As a result, when you use them in highly multithreaded contexts, you can get minimal copying overhead and mimimal memory use and minimal time spent in thread locks. It can actually improve performance substantially in such contexts.

  • @dansanger5340
    @dansanger5340 3 ปีที่แล้ว +1

    What is meant by "Persistent Data Structures"? Is that just data structures that can be persisted to disk (for example), or is there some specific meaning?

    • @YuFanLou
      @YuFanLou 3 ปีที่แล้ว +6

      Persistent data structure is a way to simulate mutation on top of immutable data structure. It persists history in exchange for a bit of speed and memory.

    • @wtetzner
      @wtetzner 3 ปีที่แล้ว +1

      This should explain it pretty clearly: en.wikipedia.org/wiki/Persistent_data_structure

    • @skyeplus
      @skyeplus 3 ปีที่แล้ว +5

      It's immutable data structures with structural sharing. When a new version of the structure is created, most of the existing nodes are re-used, usually with some form of garbage collection. The simplest example would be a list with const data element and reference internal counted nodes.

  • @M0ck3r
    @M0ck3r 3 ปีที่แล้ว +5

    I do not agree with const ints in Date example. It blocks moving these values (obviously if they were more complex types)!

  • @romanhimmes171
    @romanhimmes171 3 ปีที่แล้ว +4

    Hi fellow Developers, how do you organize non OOP between multiple teams?
    When trying to use more loose coupled concepts, other teams and even other team member have a more difficult time to grasp it. Also, OOP comes with an method 'advertisement' system built into every IDE. In my experience, free functions are more likely to be reimplemented twice and more than a member function.
    How is your experience in introducing non OOP concepts into large code bases, multiple teams with multiple different backgrounds, experience and skill levels?
    Can someone give me some pointers?

    • @T0m1s
      @T0m1s 2 ปีที่แล้ว +4

      "Also, OOP comes with an method 'advertisement' system built into every IDE" - choosing a particular paradigm because the IDE gives you a bit of help when writing "." or "->" doesn't seem like a great idea. Imagine your IDE could only give you that help for methods that have one parameter. Would you start writing only one-parameter methods? I think it makes no sense. You write your code the way you want to write it. If the IDE wants to help, then fine. If it doesn't want to help, then change the IDE. The IDE is supposed to work for you, not the other way around.
      "how do you organize non OOP between multiple teams?" The exact same way you organize OOP code. You create different directories for your modules, you put each "concept" (for lack of a better word) in a different file.
      "In my experience, free functions are more likely to be reimplemented twice and more than a member function." Even if we were to assume what you're saying is true, I don't see the problem. A little bit of code reimplementation isn't the end of the world. On the other hand, spending a lot of time trying to untangle the tightly coupled class hierarchy so that it does what you want can indeed be problem.
      "How is your experience in introducing non OOP concepts into large code bases?" You don't do that. If you're working on a large codebase then you'll most likely have to stick to the paradigm that was chosen originally. And since a big chunk (the majority?) of the industry seems to be OOP nowadays, there's little point in insisting on changing the way a team does things unless there's a huge demonstrable benefit.
      From my experience, I have seen colleagues refactor perfectly good free functions with no side effects into objects that are harder to reason about. I have seen goto-s removed and replaced with more complicated code. What did I do? I gave them a thumbs up and moved on. You have to pick your fights. A lot of our industry is just dogma and cargo culting, and it's not easy to get people to think about a problem rationally. Do experiments in your own time and see what works, and if you have any insights then share them with others.

    • @darkengine5931
      @darkengine5931 15 วันที่ผ่านมา

      We actually improved the ease of learning and maintaining our codebase (millions of LOC) substantially by moving away from OOP around a decade ago in ways all of our team members agreed. One reason is that OOP creates a boatload of coupling with its inseparable bundling of functions and data: loose coupling between abstractions in well-designed cases, but a whole lot of it which means a new developer working in any corner of the system tends to have to learn substantially more (or even exponentially more) information than minimally required to perform their task.
      Consider the example of a physicist as a new member of the team implementing a physics engine into an object-oriented architecture. He might first need to learn about scene graphs. Since scene graphs serve a wide variety of needs (audio, lights, models, cameras, textures, motion hierarchies, etc), chances are that he will need to sift through a lot of information irrelevant to his needs to understand how to work with the scene graph interface, even in the best-case scenario where the scene graph interface is designed as generally and abstractly as possible. Yet to further understand scene graphs, he must first understand scene nodes. To understand scene nodes, he must understand channels and how they link together. To understand channels, he must understand variants. To understand how to create his own scene nodes and insert them to the scene graph, he must understand the abstract node factory. To understand the abstract node factory, he must understand node descriptors. To understand node descriptors, he must understand the difference between dynamic and static descriptors. To understand how to manipulate the motion of scene entities through his physics engine, he must understand transformation objects. To understand transformation objects, ....
      I can keep going and going but I'm sure you get my point of how overwhelming this can be, and this is how large-scale OO databases can end up requiring 2 years of training a new team member with careful supervision before they can be free to confidently navigate the codebase even if their only task is to introduce new features and not even change any existing ones.
      At the end of the day, all a physics system needs to do is input motion data and either modify it (procedural) or output new motion data (functional). This simplicity is something that we can very rapidly lose if our primary paradigm for the broad-level design of a system is object-oriented. All a software does at the end of the day, down to its individual functions, is input/output or modify data. When we leave the data open rather closed in the codebase, all the physics programmer needs to learn is the barebones way to receive/access the motion data required to apply physics to objects. I'm sure you can already imagine how much simpler this can be with careful design.
      However, we lose the immediacy of maintaining invariants (or data integrity) at the immediate level of individual objects (ex: making sure a numeric data field intended to represent a quantity is never set to/initialized with a negative value). Yet that validation can be done en masse across broad phases of processing, such as performing a data integrity test after every major system has performed its data transformations for debug builds on top of writing unit/integration tests. The way to keep it manageable and avoid getting tangled up losing track of what parts of the codebase are accessing what is to think at the broader of "systems" or "engines" instead of individual functions. Build a hierarchy of data-transforming functions which, at the root level, add up to something like a "physics system" or "audio system" or "input system" with an organized and singular entry point so that you don't have to think of the data transformations of a physics system individually in terms of the 200 functions which compromise it. You shouldn't require too many of such broad entry points, and it should be very easy to reason about exactly what data each broad system is transforming if you design the systems carefully and collaborate with your team to document what central data each major system is transforming.
      This is mainly how we document and collaborate. I have next to zero knowledge how our physics engine works (I work on rendering graphics) yet all of us on the team know that our physics system just inputs motion data and outputs new motion data (which is bundled into motion component structs). It's the only system that does besides the animation system, so if there's something wrong with the shared motion data at any stage in processing, we know it's either the animation or physics system that contains the programmer mistake. This is the main way you want to be careful in your design and collaboration is with respect to data-oriented organization and documentation, since the data is no longer owned and hidden by individual objects: they're openly shared across systems.

  • @niclash
    @niclash 2 ปีที่แล้ว +5

    I walk away from this with a new title; "C++ considered harmful" as most of the criticism is not about OO per se, but how C++ is implemented and its community are using the language (i.e. quite wildly, since there are too few constraints). And else-thread mentioned "Considering there is no one definition of OOP", means I find that there are many languages that are less problematic, yet considered OO or mostly-OO, be it mainstream like Go and Rust, or very niche like the Pony Language.

    • @mikesmith9451
      @mikesmith9451 ปีที่แล้ว +1

      No. In developing games OO overall is just bad. I used a game engine called godot which just forces you to use OO, and that leads to extremely awkward interaction between objects (in a logical sense) and UI. Also performance is bad because everything contains so much redundant or outright useless data. By simply drawing textures manually on a canvas I got a 100% performance boost over using sprite nodes.
      BTW isn't Rust criticized for lack of OO which makes making GUI difficult? And Go... the "simple" language in which for something as simple as std::variant or enum in Rust one has to make interfaces.

  • @YourCRTube
    @YourCRTube 3 ปีที่แล้ว +1

    That (great) quote directly opposes message passing or even virtual dispatch, isn't it.

    • @philnash3269
      @philnash3269 3 ปีที่แล้ว +4

      "Directly opposes" might be too strong a term, but in general I tend to agree with you. But this is an area where there is a large element of essential complexity and our aim is to reduce or eliminate the accidental complexity. You are right that a message passing approach (as well as virtual dispatch) to statically known relationships is too much accidental complexity - which is why, towards the end, I was proposing that that sort of dynamic binding is best used at the component level where the trade-off is more in favour of the essential. I don't think I put that across very well and will try to do better next time - thanks!

  • @rutabega306
    @rutabega306 ปีที่แล้ว

    6:10 "Most good [videos] get maybe in the tens of thousands [of views]"
    Very meta 😂

  • @michaeljuliano8839
    @michaeljuliano8839 3 ปีที่แล้ว +2

    Kent Beck says he wishes he'd called it "Message-Oriented Programming."

    • @ladislavdobrovsky8826
      @ladislavdobrovsky8826 3 ปีที่แล้ว +1

      yes, but the original idea is much broader. in c++/java/... it just decays to a method call

  • @ThePlacehole
    @ThePlacehole 2 ปีที่แล้ว +3

    40:36 Hmm, I see the problem. If that is what your code-base looks like, then You are not doing OO.
    In OO, your "actual" object is behavior packaged with data; you are not meant to "just get the data out of it" - that's a code smell.
    Even if you are talking value types, you pass them around, not dig inside them. Any code that does is not OO, and *must not* mix with your domain.

    • @T0m1s
      @T0m1s 2 ปีที่แล้ว

      Ah yes, the famous flavour of no true Scotsman. "Ah, but THAT is not OOP". Considering there is no one definition of OOP, I think what the video shows is close enough to what we encounter in the real world, no matter what "real OO" looks like.

    • @ThePlacehole
      @ThePlacehole 16 วันที่ผ่านมา

      ​@@T0m1s Yes, unfortunately this is what we encounter, but OOP is a theory to avoid these problems. No goalposts are being moved here. If you break fundamental rules, the resulting issues aren't a consequence of OOP, they're the result of incompetence.

  • @papasmurf9146
    @papasmurf9146 2 ปีที่แล้ว +1

    The question "What is OO?" missed my favorite answer: "The application of set-theory". The value of OO is that it changes the way you think about the problem. OO's biggest failing is developers. When you have a very popular language that demands that everything be an object -- and then has the trig functions as part of the "Math" object (read library) -- it becomes pretty obvious that the designers of the language had a fundamental misunderstanding OO -- even though they mandated it throughout the language. If they don't understand OO, why would we expect a developer 2 years out of university to understand OO?
    I'm all for multi-paradigm design, but most short-comings that I've seen with OO are really short coming of the developer.

    • @williamdrum9899
      @williamdrum9899 2 ปีที่แล้ว

      Yeah that annoys me when there's a "class" for what is for all intents and purposes just a set of "workhorse" functions like that. There's no one "object" you're operating on so what's the point of making it a class to begin with? I'm not against classes as a concept, it's useful for when you have related data that you want to keep together (such as levels in a 2D video game - each would need a background tilemap, music, level size, game objectives, etc) and it would make sense to load all of that when the player selects a level)

  • @homomorphic
    @homomorphic 3 ปีที่แล้ว +8

    Late binding is stupid. 'nuff said.
    Stronger type checking is *always* a benefit.
    Runtime polymorphism is anathema. Any polymorphism should be compile time.
    Btw, in case it's not obvious, I completely agree with the message of this talk, it's just that I suffer foolish ideas with less grace than you :-)

    • @NillKitty
      @NillKitty 3 ปีที่แล้ว +5

      that means you wouldn't be able to change your method signatures (or function names) at runtime.

    • @philnash3269
      @philnash3269 3 ปีที่แล้ว +2

      Heh. I think you're a little more extreme than me. IMHO late binding has its place - I just don't think it should be the default choice - even where polymorphism is needed.

    • @babgab
      @babgab 3 ปีที่แล้ว +5

      I'm curious to know what you work on that you never encounter a case where runtime polymorphism is the most performant and maintainable choice, because I have encountered many such. Many codebases go overboard with it but let us not throw out useful tools because some people only see nails.

    • @flerfbuster7993
      @flerfbuster7993 ปีที่แล้ว +1

      @@NillKitty I mean, why'd you want to do that?

  • @ace100hyper3
    @ace100hyper3 2 ปีที่แล้ว +8

    Has a clickbait title.
    Starts with 10 minutes of completely unrelated content.
    Then mourns C++ implementation of OO compared to other languages without presenting any clear arguments.
    Ideas that classes should be objects: Classes are not objects, and that idea is not central to OO in any way. It's just an over-generaized idea which historically didn't make it because of costs and mushiness of resulting code.
    Ideas that people are moving away from virtual dispatch: Virtual dispatch is not being moved away from. As performance implications of it are more clearly understood, people who were previously reluctant to embrace it are now using it more. The effect is the opposite, and this idea is pure FUD. It is still the vastly superior way to do what it was meant to do, it's just that people are getting better at not misusing it.
    Then after the performance rant there is this idea that message passing is a better mechanism, which has its own performance problems, and cannot do anything that a method call cannot. The cost of that dispatch is hard to reason about because of implementation details, but it is worse than previously criticized virtual dispatch.
    "We are paying for many of the penalties of using virtual dispatch and not getting any of the benefit." Where do I begin...
    - We are not paying for virtual dispatch because method are non-virtual by default, in contrast to Java where it is the default and message passing where we always pay for higher costs.
    - The benefit that it provides is the benefit that is actually needed. If we wanted full message passing, we can build it ourselves.
    - Using message passing is error prone. Wikipedia: "The Smalltalk-style programming as used in Objective-C allows messages to go unimplemented, with the method resolved to its implementation at runtime." I gess we need some "undefined is not a function" in our C++.
    The attitudes on method swizzling that I've found "Swizzling is widely considered a voodoo technique, prone to unpredictable behavior and unforeseen consequences. ... Swizzling a method and not calling the original implementation may cause underlying assumptions about private state to break..." This makes programs more dynamic, which is in contrast to the point from the original paper.
    Swift has different string implementation behind the same type. www.swift.org/blog/utf8-string/ This makes every access very slow because at runtime the type needs to determine what it's implementation is. This is a form of type weakening in order to keep the same interface. And no, it's not "quite powerful when you think about it what way" because we can do the same thing manually and we generally don't do it because it's a bad idea.
    Yeah, stronger type checking is totally not a benefit :)
    Can't comment on type erasure, but protocols in swift are just interfaces which can be attached after the fact. They are resolved with interface dispatch, which is much slower than regular virtual dispatch. In Java and C# this process involves looking up a type, then looking up a v-table pointer for that type in a table of those. The benefit is that every object gets only one v-table pointer. This is so costly that it usually involves 2 levels of caching. Not quite sure how it is implemented in Swift, but it's probably along the same lines. C# does not allow patching interfaces for correctness reasons, the resolution selection is bound to lead to mistakes, as these rules are faily involved. In Swift they just don't care, and there are many articles describing confusing properties of when calls are virtual and when they are not.
    Rust implements "trait objects" with fat pointers: one pointer to the object and another to the v-table which is resolved when the object is converted to trait. It is faster than C++ interface because that resolution is done only once (not once per method call). It has a drawback with certain variance characteristics. This performance benefit has nothing to do with reference types, as value types in C++ are perfectly polymorphic as long as they are accessed through a pointer. In that respect C++ is equivalent to Rust.
    Mixins are not rust traits, that's also FUD. Mixins imply adding state, which has severe problems that most decent language don't implement it (one exception is D). Traits can only add behavior to themselves (not the other object) and cannot have state.
    "Inheritance has a lot of problems..." it has a problem that it is prone to misuse. The fact that C++ uses inheritance to implement interfaces has nothing to do with inheritance. Other than that, single inheritance is great. Where was the argument again?
    Access controls on file level are an atrocity. That design ties a physical structure (file) to a logical structure (type) and suddenly files become less refactoring friendly. Languages that do this don't have friend feature. What people instead wanted was finer grained access modifiers (not abolishing access modifiers). Friend is a far better concept.
    I don't know if I'm going to make it until the end of this video, but protected is not broken. It does what it is meant to do. It is the job of the base class to ensure that protected is appropriate. It is true that a deriving class could leak state or transitively gift invariants. In practice this never happens because protected is just a notch lower than public and has to do with convenience (not burdening the public API) rather than safety.
    We use string builders in C# and they are a nuissance. In C++ they could be implemented much more efficiently as the author states, but the conversions to and from would be ceremony on the user side. Extrapolated, this could lead to all types being duplicated with mutable builders, then moved to immutable, which is a disastrous ceremony. COW was a much better idiom for value semantics, but was abandoned in favor of value strings.
    Immmutable data structures lead to horrific inefficiencies, especially collections. This style of thinking is much more appropriate for a less performance conscious language like C#. There is no such things as "cheap copy". Linked lists are efficient to copy as immutable, but access and iteration times are horrendous. It is the most cache unfriendly data structure. Every immutable data structure has to make a trade-off between copy times and locality of data. However it is turned, the costs are considerable. The fact this thing is formalized doesn't make it good.
    Getting closer to the end, luckily. STM was a hot research topic in early 2000s. It is so complicated, costly and has no universal semantics. It has no killer use case. It's one of those things that researchers go to because they want to solve complex problems, not because somebody asked to have this feature. Nobody is seriously expecting STM to make a comeback.
    I don't even like C++, and it does many things wrong, but these things are mostly not in this video. Considering costs and features, in most respects C++ makes the right tradeoffs when it comes to dispatch.
    I really don't like the idea of bashing OO with these very flimsy arguments in order to support some kind of more functional approach.

    • @chrimony
      @chrimony 2 ปีที่แล้ว

      The mention of transactional memory was definitely jarring and a red flag. As was "monkey patching". Much of this talk feels like I went back in time to when languages like Haskell and Ruby became fads. You mentioned you don't even like C++ -- do you have a favorite language to work in (beyond "right tool for the job")?

  • @tourdesource
    @tourdesource ปีที่แล้ว

    Not trying to offend the speaker, but this talk was extremely scatterbrained.

  • @zofe
    @zofe 3 ปีที่แล้ว +1

    1. Goto instructions are inherently dangerous simply because they create multiple entry-points to executable code / functions, which is atrocious to analyze.
    2. Static-relations model the world being addressed by means of a computer program, thus need depict their behavior inherently and coherently, thus OO is a trivial must.

    • @dagoberttrump9290
      @dagoberttrump9290 ปีที่แล้ว

      I disagree with both dijkestras points:
      - 1) goto is usefull in some places where other control structures would make code more _unreadable_, unperformant or heavy on the stack
      - 2) things do change in the real world, this is just how it is. It is not complicated however to keep track of what is going on if you respect the _invariants_ of the type which should be firmly established. Define what is mutable and what not and how it correlates.

  • @porky1118
    @porky1118 3 ปีที่แล้ว

    I think, the worst thing about OO is the ability to override existing methods.
    If you have to override a method without calling the overwritten method, it's most likely bad design.
    So it should not be possible to do this.

    • @babgab
      @babgab 3 ปีที่แล้ว +8

      That's... the entire point of virtual functions in the first place. And if by "override a method without calling the overwritten method" you mean to say that every overridden method should "call super"... You're in disagreement with almost everyone else in the industry, because that's widely considered an antipattern or at least a design smell.