OOP is dead



  • @Gąska said in OOP is dead:

    OK and now explain Node. By now JS has become the leading programming language for all kinds of server-side programs, as well as native desktop and mobile apps, and none of that has anything to do with HTML.

    The alternative was PHP.


  • BINNED

    @dfdub said in OOP is dead:

    @topspin said in OOP is dead:

    pointer arithmetic

    Serious question: When's the last time you even used pointer arithmetic?

    In a modern (≥ C++11) code base, if you're doing pointer arithmetic at all, that's probably a code smell.

    Depends what we're talking about. The example of @Mason_Wheeler doesn't even compile, so that's out. Anything more complicated than casting to char*, probably not at all. But indexing into a raw C array is "pointer arithmetic" already, and raw C arrays still exist, even with std::array around.
    Also, to answer the OP, a grep for dynamic_cast gives zero results (in first-party code), but my current code-base is absolutely tiny.

    That wasn't my point though. If you're a freshman struggling to understand the concept of a pointer, fine, but an actual developer? Bad sign.



  • @Gąska said in OOP is dead:

    @topspin said in OOP is dead:

    I’ve read some valid complaints here about things wrong with C++, but not really about how C++ gets OOP wrong. Other than “you can do things I don’t want to do.”

    Quick rundown from someone who doesn't arbitrarily exclude JS from the family of OO languages:

    • For inheritance to work properly, the base class needs a virtual destructor and no copy constructor. C++ defaults to a non-virtual destructor and an autogenerated copy constructor.

    The base class needs a virtual destructor and a correct copy constructor. In the ideal, it shouldn't need more than the automatic one the compiler will generate, but if you want the derived class to be copyable, the base class must not have:

      class Base
      {
        //...
        Base(const Base &) = delete;
      };
    
    • Multiple inheritance sounds nice in theory but is a total shitfest in practice. And the details of how virtual inheritance works would make a good basis for an H. P. Lovecraft book.

    Some of the hairiest things in multiple inheritance arrive when you have diamonds going on, but outside that, there's only really one bit of strangeness. If one base class declares a pure-virtual function, and another (coincidentally or not) declares a concrete function with the same signature, in C++, that concrete function will act to override the pure-virtual. The effects will be ... interesting, and the warnings from the compiler will be a source of amusement, because that override mechanism is called "domination". ("Method so-and-so from base class B1 overrides method so-and-so in base class B2 by domination"...)

    But virtual inheritance is only required if you want to close the diamond. (Calling it "virtual" inheritance ties the concept to C++, but the diamond is a general problem for any language that has multiple inheritance - sometimes you actually don't want to close the diamond, and sometimes you don't care. At least C++ gives you the option.)



  • @topspin said in OOP is dead:

    But indexing into a raw C array is "pointer arithmetic" already

    Indeed. People tend to forget that A[i] is no more and no less than syntactic sugar for *(A+i), and that as a consequence, if A is a normal pointer or C-style array and i is an ordinary numeric PoD type, it is also equivalent to i[A]. I have a special crate of rounds for the GAU-8 reserved for people who write that sort of thing without good reason, and I feel that I should add that there isn't a good reason.



  • @Steve_The_Cynic said in OOP is dead:

    Some of the hairiest things in multiple inheritance arrive when you have diamonds going on, but outside that, there's only really one bit of strangeness. If one base class declares a pure-virtual function, and another (coincidentally or not) declares a concrete function with the same signature, in C++, that concrete function will act to override the pure-virtual. The effects will be ... interesting, and the warnings from the compiler will be a source of amusement, because that override mechanism is called "domination". ("Method so-and-so from base class B1 overrides method so-and-so in base class B2 by domination"...)

    That's a bit over-simplified and makes it seem worse than it actually is. For one thing, to get "dominance", the class with the concrete function has to virtually inherit from the one declaring the pure-virtual function, and so does the final class, I. e. you really need to have an actual inheritance diamond. Without it, the two functions on the derived class are distinct functions, and trying to call any of them without specifying which one you mean is a compile error ("ambiguous access" in VS). So it's really like overriding in the general case, just applied to the particularities of virtual inheritance. Seems to me you'd have to go out of your way to hit this accidentally.

    Dominance can actually be used for a kinda interesting coding pattern to cleanly separate interfaces and their implementations. The basic idea is the following:

    class AnInterface
    {
    public:
        virtual ~AnInterface() {}
        virtual void foo() const = 0;
    };
    
    class AnImplementation : public virtual AnInterface
    {
    public:
        void foo() const override { std::cout << "foo" << std::endl; }
    };
    
    class SomeSpecificThing : public virtual AnInterface, private AnImplementation
    {
    public:
        using AnImplementation::foo;
    };
    
    
    int main()
    {
        SomeSpecificThing thing;
        AnInterface const& interface = thing;
        interface.foo(); // <- calls AnImplementation::foo()
    }
    

    This allows you to implement AnInterface on SomeSpecificThing by "borrowing" the ready-made implementation provided by AnImplementation, while keeping that fact private. So if, at some point in the future, AnImplementation is no longer appropriate for SomeSpecificThing, you can switch to a different implementation (by using a different implementation class or just implementing it directly) without having to track down thousands of places which now assume that SomeSpecificThing is always an AnImplementation.



  • @dfdub said in OOP is dead:

    Ugh, I should have known you'd make a snarky comment. Obviously, C++ is statically typed and already makes sure arguments are type-compatible. But sometimes you want to forbid arguments even though their types would be compatible with the signature.

    :wat:

    You don't want anyone to accidentally call your factorial function with a signed integer argument (because you want to avoid cases where the integer variable is accidentally negative)

    And instead you force everyone calling the function to cast their ints to uints before calling it? (And make sure that their ints aren't negative when they do so, of course.) That's not fixing the problem; it's just moving it elsewhere.

    and you certainly don't want anyone to call it with a double argument, thinking it'll do something useful for floating point values. These conversions can be prevented by defining deleted overloads:

    unsigned int factorial(int) = delete;
    unsigned int factorial(double) = delete;
    

    The same way, you can prevent all kinds of otherwise implicit conversions, not just the conversions between built-in types that C++ unfortunately inherited from C.

    :wtf_owl: :wtf_owl: :wtf_owl:

    So the language provides information-destroying implicit conversions, and the solution is to add a bizarre additional layer of complexity to an already crazy-complex overload resolution system ⁉

    The proper solution is to abandon the octopus-dog and get a language that was actually designed.

    A probably slightly more common use case: You can also use this feature to prevent dangling references. Let's say you have a class ConfigurationFile that represents what you read from your configuration. Certain configuration keys are optional, so provide the following method:

    struct ConfigurationFile {
      const std::string& getOrDefault(const std::string& key, const std::string& defaultValue);
    };
    

    Looks reasonably, but now you've made it easy to write buggy code:

    You haven't made it easy to write buggy code; Bjarne Stroustrup did.

    auto& value = myFile.getOrDefault("foo"s, "bar"s);
    

    The first argument is fine, but the second argument absolutely shouldn't be a temporary, because if the configuration file doesn't contain a value for foo, the return value of getOrDefault() is a dangling reference to where the temporary string used to be.

    ...yeah. Once again, "objects as value types" is a thing that should not exist. Fix that, and the problem of "dangling reference to temporary stack object" goes away, because there is no such thing as a temporary stack object.

    "All new features added to C++ are intended to fix previously new features added to C++." -- David Jameson


  • BINNED

    @Mason_Wheeler said in OOP is dead:

    because there is no such thing as a temporary stack object.

    Yeah, no thanks. I'd rather prefer rust's solution of lifetime checks at compile-time than the "I'm taking your tools away" solution.



  • @topspin :rolleyes: A footgun is not a tool.


  • Discourse touched me in a no-no place

    @Mason_Wheeler said in OOP is dead:

    Once again, "objects as value types" is a thing that should not exist.

    Strictly, it's a thing that should only exist when the compiler proves that it is safe to do so through things like use analysis, and would be an optional optimization (typically for LTO rather than initial compilation). During LTO it's not that difficult to track such things. “Premature optimization is the root of all evil” after all, though I'm sure this wasn't the sense in which Knuth wrote that.

    Note that this is effectively what Java and C# both do.



  • @Mason_Wheeler said in OOP is dead:

    A footgun is not a tool.

    A footgun is only a footgun if you don't bother to check what it aims at before pulling the trigger.


  • BINNED

    @Mason_Wheeler said in OOP is dead:

    @topspin :rolleyes: A footgun is not a tool.

    The stack is a tool. Scoped lifetimes and RAII are tools. But sure, box all your integers on the heap if you want to.


  • Discourse touched me in a no-no place

    @topspin said in OOP is dead:

    @Mason_Wheeler said in OOP is dead:

    because there is no such thing as a temporary stack object.

    Yeah, no thanks. I'd rather prefer rust's solution of lifetime checks at compile-time than the "I'm taking your tools away" solution.

    Bounding the lifetime of an object to a scope/stack frame is a good thing. Bounding the active lifetime also has value (but is slightly different; the object still exists afterwards and this is useful in some cases). But these things should merely enable placement on the stack, not enforce it. Most of the time, you don't want exact placement of objects (and the cases where you really need it are usually ones where code portability is a non-issue).



  • @topspin said in OOP is dead:

    @Mason_Wheeler said in OOP is dead:

    @topspin :rolleyes: A footgun is not a tool.

    The stack is a tool. Scoped lifetimes and RAII are tools. But sure, box all your integers on the heap if you want to.

    Holy strawman arguments, Batman! Since when are integers objects?



  • @dkf said in OOP is dead:

    @Mason_Wheeler said in OOP is dead:

    Once again, "objects as value types" is a thing that should not exist.

    Strictly, it's a thing that should only exist when the compiler proves that it is safe to do so through things like use analysis, and would be an optional optimization (typically for LTO rather than initial compilation). During LTO it's not that difficult to track such things. “Premature optimization is the root of all evil” after all, though I'm sure this wasn't the sense in which Knuth wrote that.

    Note that this is effectively what Java and C# both do.

    OK, proper :pendant: version: objects as conceptual value types is a thing that should not exist. If the compiler can prove it's a safe optimization in specific cases, more power to it.


  • Discourse touched me in a no-no place

    @Mason_Wheeler said in OOP is dead:

    proper :pendant: version

    That's pretty much always the right thing round here.


  • BINNED

    @Mason_Wheeler said in OOP is dead:

    @topspin said in OOP is dead:

    @Mason_Wheeler said in OOP is dead:

    @topspin :rolleyes: A footgun is not a tool.

    The stack is a tool. Scoped lifetimes and RAII are tools. But sure, box all your integers on the heap if you want to.

    Holy strawman arguments, Batman! Since when are integers objects?

    Yeah, because your footgun arguments are much better. Oh wait, they're not arguments at all.
    At least my arguments aren't based on linking to some post which itself only links to a collection of irrelevant quips, including one from myself! :rolleyes: ^ :wtf-whistling:

    Also, in an OOP language, shouldn't everything be an object?



  • @Mason_Wheeler C++ is a low-level language and things like overlaying an object with a byte array only make sense if an object is a value type.


  • ♿ (Parody)

    @BernieTheBernie said in OOP is dead:

    @dfdub said in OOP is dead:
    "That's it, folks. OOP is officially dead."

    But it did not die.

    Status: Finally giving in to the urge I have every time I see this topic:

    "@dfdub is dead"
    – OOP



  • @Mason_Wheeler said in OOP is dead:

    So the language provides information-destroying implicit conversions

    I knew you'd complain about the example and ignore that there may be other use cases. Which is why I explicitly mentioned it's just an artificial example and that the same principle can be applied to other conversions. (I even chose an example that highlights some of C++'s flaws, because I actually try to argue in good faith, unlike others.)

    Judging from your reaction, it's a good thing I didn't try to anonymize the code I actually wrote last month to illustrate the concept, because the effort would obviously have been wasted.


  • ♿ (Parody)

    @dkf said in OOP is dead:

    @Mason_Wheeler said in OOP is dead:

    @dkf said in OOP is dead:

    @Mason_Wheeler said in OOP is dead:

    I often see this notion raised as an article of faith

    Basically, when you see someone using inheritance where composition would be better, you've got a candidate for :wtf:.

    Yes, the "favor composition over inheritance" slogan is always brought out to defend the "inheritance is evil" slogan, but again, never with any evidence behind it. To me, a programmer saying "favor composition over inheritance" makes exactly as much sense as a carpenter saying "favor drills over saws." They're very different tools that do different things, and the competence of anyone who seems to think that they can be used interchangeably ought to be called into question.

    :thats_the_joke:

    Another over-inheritance antipattern is where someone inherits from, say, Button just to add a particular label to it. Inheritance for the purpose of configuration is just horrible.

    Why? I'm amused by this, because I remember being at a conference where the speaker made exactly the opposite point: being able to create specialized components like an "OKButton" (a standard Button subclass whose text says OK and whose Modal Result action is hard-coded to return the OK enum) or a "CancelButton" (same thing, different details) is an awesome thing because when you create so many of these in your day-to-day work, it adds up to a big time saver. (I'm not completely convinced by his point, but at least he provided a solid rationale for it. All I'm hearing from you is not to do this because it's "just horrible," whatever that means.)

    For the timesaving argument to be true, either configuring those buttons is a complicated thing (:wtf:) or the speaker finds configuring things to be difficult (:facepalm:). But ease of configuration does depend on having some way to plug in code to do the reacting to the button being activated (clicked, etc.) and when doing that is difficult I can see the point. And also a place where the containing language is making programming harder than it should.

    The button thing in particular sounds to me like the same argument for using CSS classes to style your html over inline styles. Or having any other bit of reusable code, really.

    I get the "ick" factor of doing things at such a granular level but at a certain point it's just a matter of taste.



  • This post is deleted!


  • @Steve_The_Cynic said in OOP is dead:

    it is also equivalent to i[A]. I have a special crate of rounds for the GAU-8 reserved for people who write that sort of thing without good reason, and I feel that I should add that there isn't a good reason.

    The [] act as a free set of parentheses. That is, (complicated expression)[i] => i[complicated expression]. :tro-pop:

    I've only ever used this feature to troll people. Sometimes it's even deserved.



  • @Mason_Wheeler said in OOP is dead:

    ...yeah. Once again, "objects as value types" is a thing that should not exist. Fix that, and the problem of "dangling reference to temporary stack object" goes away, because there is no such thing as a temporary stack object.

    Except the problem is completely unrelated to the fact that there is an object involved. Replace std::string with a primitive type (take int, since you later ask why an integer would be an object), and the same problem exists.


  • Notification Spam Recipient

    @Zenith said in OOP is dead:

    @DogsB When you stay "static utilities," do you mean DLLs with static functions or static analysis of code?

    Nothing that fancy. Methods that can be accessed without instantiating a class. YMMV but when most of an apps logic takes place in them it's usually a sign that a codebàse is going south.



  • @dfdub said in OOP is dead:

    @Mason_Wheeler said in OOP is dead:

    So the language provides information-destroying implicit conversions

    I knew you'd complain about the example and ignore that there may be other use cases. Which is why I explicitly mentioned it's just an artificial example and that the same principle can be applied to other conversions.

    ...is that what you got out of it? Because what I actually wrote is that the principle itself is something that wouldn't need to exist if it weren't for underlying flaws in the C++ language.

    (I even chose an example that highlights some of C++'s flaws, because I actually try to argue in good faith, unlike others.)

    And that's appreciated. I just find that the flaws it highlights tend to underscore my thesis: that C++ is a fatally flawed language that ought to be abandoned.



  • @cvi said in OOP is dead:

    @Mason_Wheeler said in OOP is dead:

    ...yeah. Once again, "objects as value types" is a thing that should not exist. Fix that, and the problem of "dangling reference to temporary stack object" goes away, because there is no such thing as a temporary stack object.

    Except the problem is completely unrelated to the fact that there is an object involved. Replace std::string with a primitive type (take int, since you later ask why an integer would be an object), and the same problem exists.

    Only in C++, though. Pass an int by reference in C# or Delphi and store the passed value in some persistent location, and no corruption or dangling ensues.


  • kills Dumbledore

    @dfdub said in OOP is dead:

    I only created this thread instead of posting in WTF Bites because I recently complained about the lack of new threads in the side bar and didn't want to be a hypocrite. But it's definitely more fun to watch a new thread get derailed than to watch old arguments in an old thread. We should do this more often.

    Since the pointless arguments have already started, I might as well ask:

    @Mason_Wheeler said in OOP is dead:

    What exactly does heavy use of a specific feature of a language that does an abysmal job of implementing OOP concepts in the first place have to do with the principle of OOP being "dead"?

    How exactly does C++ implement OOP badly, in your opinion?

    It's just C with classes innit



  • @Mason_Wheeler said in OOP is dead:

    Only in C++, though. Pass an int by reference in C# or Delphi and store the passed value in some persistent location, and no corruption or dangling ensues.

    What happens if you compute an int in a function and return it by reference? Same problem, slightly different setup.

    Edit: To slightly expand, that would make storing the result in a persistent location difficult, due to recursion, or possibly threading and so on.


  • Discourse touched me in a no-no place

    @cvi said in OOP is dead:

    Same problem, slightly different setup.

    At least an int has a memory layout defined by the Platform ABI and that's not too likely to change without everything needing to be recompiled anyway.



  • @cvi said in OOP is dead:

    @Mason_Wheeler said in OOP is dead:

    Only in C++, though. Pass an int by reference in C# or Delphi and store the passed value in some persistent location, and no corruption or dangling ensues.

    What happens if you compute an int in a function and return it by reference? Same problem, slightly different setup.

    Last I heard, Delphi doesn't have by-ref returns. (It may have changed, but I doubt it.) C# does, but the rules for ref returns make trying to return a reference to a local that would end up as stack garbage a compile error.



  • @dkf A lot of other types also have de-facto fixed memory layouts. Fixed-size vectors, e.g., float4, spring into mind. We rely quite heavily on such types, and that's largely made possible by inlinable code and value semantics. You could of course push them into the language itself as primitive types, but few languages seem to do that.

    The other side of the coin is that if you resign to having your objects on the heap, you can very easily define objects whose size/layout can change without recompiling everything.



  • @cvi said in OOP is dead:

    @dkf A lot of other types also have de-facto fixed memory layouts. Fixed-size vectors, e.g., float4, spring into mind. We rely quite heavily on such types, and that's largely made possible by inlinable code and value semantics. You could of course push them into the language itself as primitive types, but few languages seem to do that.

    Pretty much every shader language ever has a 4-float vector as a built-in type. (May or may not :technically-correct: be a primitive, but it's fundamental to many built-in use cases and is always accessible without having to import anything.)



  • @Mason_Wheeler I'm actually curious how some of the linked examples work under the hood.

    I'll probably need to spend a bit more time to read it carefully, but from quickly skimming, you need to declare ref when defining variables that might be returned (passed by? is that an option?) by reference. I obviously haven't had the time to dig into the produced code, but to me that smells like giving permission to move the object to the heap.



  • @Mason_Wheeler said in OOP is dead:

    Pretty much every shader language ever has a 4-float vector as a built-in type.

    Yes, those were the few languages that I know that are doing it.

    You typically want to have the same set of types on the host side of the application. Even if shaders aren't involved, vec2/vec3/vec4 are common enough if you deal with anything in 2D, 3D. Even with shaders, keeping "uniform" computations on the host (instead of pushing them into the shader) is pretty much always a win.

    (I'll omit the rant about current shader languages sucking for now.)



  • @cvi You can declare ref variables and return them, or you can return a reference to something else. You can't return a reference to the stack, though, and the language won't automagically move stuff to the heap to let you return a reference to a stack location. (Go does this IIRC.) So basically what you need to return is a reference to something that was already on the heap. (It's more complicated than that, but that's the oversimplified TLDR version.)



  • @Mason_Wheeler That is roughly what I would have guessed.

    There's a somewhat interesting parallel to C++'s value types (especially in the context of stuff like float4). Passing through/returning by reference will typically require the value to be materialized in memory (automatic storage/"stack"); this is actually something one would want to avoid forcing to happen in hot code, where it's preferable to have stuff stay in registers (or not exist at all).



  • I feel like the C++ stuff is a different thread ...

    On composition vs inheritance (from way back on page 1): The reason that people abuse inheritance when composition would make more sense is that most languages make it hard to build an object by composition in a way which is nice to use.

    Let's say I have a base class A:

    class A {
     f();
     g();
     property x;
    }
    
    some_code() { a = new A(); a.f(); return a.x; }
    

    If I want something which is 'an A, but a bit more', it's really easy to construct that with inheritance:

    class B: A {
     h();
    }
    some_more_code() { b = new B(); b.f(); b.h(); return b.x; }
    

    But if I want to compose A, I either have to delegate everything I still want public:

    class C {
     A a;
    
     h();
    
     f() { a.f(); }
     g() { a.g(); }
     // etc
    }

    ... or my calling code has to be explicit about the composition:

    yet_mode_code() { c = new C(); c.a.f(); c.a.h(); return c.a.x; }

    As well as being annoying and verbose, this means it's much harder to refactor using composition (because you break existing client code).

    It would be really nice if there were a way to say 'this class is a composition, but it is mostly an A, so delegate public calls of A onto my interface too'.



  • @ixvedeusi You say that, except that I've seen the domination message in a hierarchy with no virtual inheritance (but interface-style multiple inheritance), and it was virtual functions called via one of the base classes.

    All of the base classes declared a debugging aid:

        virtual std::string getDescription() const = 0;
    

    Except that one of them had a concrete implementation, while the most-derived class did not. Compiling the most-derived class generated the message until I overrode the function in the most-derived class.

    This was C++98 compiled with Visual C++6, so it may have been a Microsoft bug, of course.


  • Discourse touched me in a no-no place

    @bobjanova said in OOP is dead:

    It would be really nice if there were a way to say 'this class is a composition, but it is mostly an A, so delegate public calls of A onto my interface too'.

    The problem is if the composition isn't an A (or a subtype of it) then there's no implied contract on the methods. Still, there are object systems that have much better support for delegation than C++, Java or C#. I'm aware of an object system that lets you say:

    # You might disagree with the precise syntax, of course
    delegate method * to memberObject;
    

    as part of the class definition (with * being a glob match for method names, for convenience). The class machinery takes care of the rest.

    There are far more different ways of doing OO than most developers seem to think. I tend to think that until people have written two or three of them, they don't really know what they're doing on the topic; there are some really complex design decisions that you can't really understand until after you've made the key mistakes involved. 😜



  • @dfdub said in OOP is dead:

    A probably slightly more common use case: You can also use this feature to prevent dangling references. Let's say you have a class ConfigurationFile that represents what you read from your configuration. Certain configuration keys are optional, so provide the following method:

    struct ConfigurationFile {
      const std::string& getOrDefault(const std::string& key, const std::string& defaultValue);
    };
    

    Looks reasonably, but now you've made it easy to write buggy code:

    Sorry, no, that doesn't look even vaguely reasonable. It returns a reference to ... something, but without a guarantee that the something will continue to exist afterwards. Combined with a call that keeps a returned reference as a reference, you'll get what you deserve, which is a big dose of nasal demons. (Think: what happens if key exists in the config, but the ConfigurationFile object gets destructed while myFile is still around?)



  • @dkf said in OOP is dead:

    delegate method * to memberObject

    That's essentially what I'm saying would be nice in mainstream languages, yes. And sure, in a pure system "there's no implied contract on the methods" but it can still be really nice in a pragmatic world.



  • @bobjanova said in OOP is dead:

    It would be really nice if there were a way to say 'this class is a composition, but it is mostly an A, so delegate public calls of A onto my interface too'.

    There is: inheritance! :half-trolleybus-l:



  • @DogsB said in OOP is dead:

    Nothing that fancy. Methods that can be accessed without instantiating a class. YMMV but when most of an apps logic takes place in them it's usually a sign that a codebàse is going south.

    I think that "YMMV" qualifier is new. Without it, I was going to say that I could not possibly disagree more.

    Static functions force you to be alot smarter with your design because you don't get to carry so much state that what should be discrete steps turns into a pile of spaghetti.


  • Discourse touched me in a no-no place

    @Zenith said in OOP is dead:

    Static functions force you to be alot smarter with your design because you don't get to carry so much state that what should be discrete steps turns into a pile of spaghetti.

    Sometimes they help. Sometimes they hinder.


  • Notification Spam Recipient

    @Zenith said in OOP is dead:

    @DogsB said in OOP is dead:

    Nothing that fancy. Methods that can be accessed without instantiating a class. YMMV but when most of an apps logic takes place in them it's usually a sign that a codebàse is going south.

    I think that "YMMV" qualifier is new. Without it, I was going to say that I could not possibly disagree more.

    Static functions force you to be alot smarter with your design because you don't get to carry so much state that what should be discrete steps turns into a pile of spaghetti.

    I actually agree with that but you can acheive that with ordinary objects too. I prefer going with objects because they're easier to mock for unit tests and you have to think more about design. With statics it becomes a pile of spaghetti faster because you can call any method from anywhere making code a pain to navigate.


  • Discourse touched me in a no-no place

    @DogsB said in OOP is dead:

    With statics […] you can call any method from anywhere

    :sideways_owl: That's addressed by protection levels and scoping and so on.



  • @Steve_The_Cynic said in OOP is dead:

    sometimes you actually don't want to close the diamond

    Well, it's hard to polish a diamond that doesn't have a closed surface.


  • Notification Spam Recipient

    @dkf said in OOP is dead:

    @DogsB said in OOP is dead:

    With statics […] you can call any method from anywhere

    :sideways_owl: That's addressed by protection levels and scoping and so on.

    Ha! Maybe with the people you work with. My recent experience has put me with people that get confused by sub class only access. Public modifiers everywhere. I dread to see what they could do with à goto statement.



  • @Steve_The_Cynic said in OOP is dead:

    Sorry, no, that doesn't look even vaguely reasonable.

    Ignore the default value for a second. From an interface perspective, what would be more reasonable as the return value of the member function? Do you want to copy the whole string every time someone queries a configuration value?

    The configuration file object is the obvious owner of the configuration values, so the fact that the returned references will only be valid as long as the configuration object should be pretty obvious to the caller.


  • Java Dev

    @dfdub said in OOP is dead:

    @Steve_The_Cynic said in OOP is dead:

    Sorry, no, that doesn't look even vaguely reasonable.

    Ignore the default value for a second. From an interface perspective, what would be more reasonable as the return value of the member function? Do you want to copy the whole string every time someone queries a configuration value?

    The configuration file object is the obvious owner of the configuration values, so the fact that the returned references will only be valid as long as the configuration object should be pretty obvious to the caller.

    I've got quite a bit of management code for that, doing reference counting to keep the old configuration in memory until everything has switched to the new file. Plus of course picking up new files, loading them in the background, rejecting them if they're invalid, communicating the fact that a new configuration is in memory to the worker threads, etc.


Log in to reply