9 ms·
Like you say, the main benefit of OOP is that it matches the way we perceive reality. That can make it intuitive to design software. However, this often comes a
by zmb_ 12y ago
Like you say, the main benefit of OOP is that it matches the way we perceive reality. That can make it intuitive to design software. However, this often comes at the expense of performance since the hardware that executes your system bears no resemblance to the way we perceive reality.
Take your physical simulations, for example. You may have a game scene with hundreds or thousands of entities. It's natural to try to model these as objects. In every frame you will then call a method on each object that applies a translation to its position. Very intuitive and simple. But unfortunately extremely inefficient for a computer to execute. You will likely pay a number of cache misses while jumping to each of your objects. And once you get there, the rest of the cache line where your coordinates are is full irrelevant stuff and therefore wasted (the other fields of your object).
For performance, you would want to have all your entities' coordinates in continuous memory one after another, and then apply a function to translate them all at once. Pretty much the opposite design from where OOP naturally leads you.
- the_af 12y agoIt's debatable whether OOP "matches the way we perceive reality". Many have come to believe this, but is it true? I don't perceive all of reality as a strict hierarchy of things constructed out of templates, with "is a" and "has a" relationships between them. I don't think the main problem with OOP is performance, either. The problem is that it's not always the right or the most natural design approach, regardless of any performance considerations.
- bunderbunder 12y ago> It's debatable whether OOP "matches the way we perceive reality". Many have come to believe this, but is it true? No, it isn't true. But neither do we perceive all of reality as a collection entities that never change but are used to create new unchanging entities by a series of idempotent mappings from domains to codomains. But both abstractions are useful as ways to organize your thoughts, or as languages we can use to describe processes in an organized manner. Which one is more useful is often case specific - with the case covering not only the problem space but also the individual psychology of the person doing the work. Many find that neither is so useful that they would be comfortable using it to the exclusion of the other, which is why there are so many functional languages with object-oriented features such as Caml and why so many major object-oriented languages such as C++, C# and Java have been acquiring increasingly many functional features over the past decade.
- tolmasky 12y agoI'm not sure why immutability is being lumped into OOP alternatives, seems orthogonal (to me). You can have immutable AND mutable versions of OOP after all. Similarly, you can have immutable and mutable composability. To me the main issue with OOP not matching reality is having people put things into hierarchies instead of thinking of common elements found in them. For example, to me thinking about composing objects in Go and its nice-auto-applying interfaces is similar to Haskell's data with typeclasses, despite one being totally mutable and the other immutable (obviously these feature sets are quite different, my point is in how you think about "the world", i.e. a circle in both Go and Haskell doesn't inherit from anything but may abide by a contract thanks to either interfaces or typeclasses, getting you to talk about things in terms of its features and not its inheritance).
- jakevn 12y agoInheritance is not necessary for OOP. For example, if you have: struct Cat { meow: String } impl Cat { fn talk(self) { println!(self.meow); } } fn main() { let bob = Cat { meow: "Mroowwwww!" }; bob.talk(); // Prints "Mroowwwww!" Good job, bob. } Cat lumps the data and its associated functionality together into an object. This is object-oriented programming even if you never bring inheritance into the picture. Both Go and Rust have objects, they simply go by the name struct. Haskell does not follow the object-oriented approach. I think the confusion surrounding OOP is that people associate OOP with a particular implementation of it, like Java.
- tolmasky 12y agoSure, but with that wide a definition its really hard for a programming language not to be object oriented. C can be just as object oriented with structs, unless you consider bob.talk() to be just completely conceptually different from talk(bob) where talk's definition expects a Cat struct. Similarly, under your example, Haskell is also object oriented since you have data types with fields and functions that can only operate on that kind of data (Again at this point you'd really be arguing that the order of function/caller is the "OOP differentiator" since (ignoring inheritance), there is no difference between Cat's talk method and a talk function that takes in a Cat). In other words, I don't think anyone is ever arguing against organization of data and functions into more abstract "types". Pascal does this with Records, C with structs, Haskell with data types, C++ with classes, JavaScript with prototypes, etc etc. So if thats all it takes to be "OOP" (in other words, not forcing you to only use ints and floats), then I guess I agree that OOP is a better representation of the world. But now I'd argue that the more interesting discussion is between the has-a and is-a versions of this.
- seanmcdirmid 12y agoIs-a and has-a, and more to the point, naming things, is just an outgrowth of our natural linguistic capabilities. The alternative is anonymous immutable values with structural comparisons (aka functional programming), which we've only been studing as math for a couple of thousand years or so (vs. Around 100k years for language).
- the_af 12y agoI don't know if the alternative is functional programming or structural comparisons, and I wasn't arguing that in this particular case. I agree about naming things, but I don't consider it an OO exclusive. What I'm saying is that there are a couple of assertions, more or less accepted in the industry, that may be flawed or simply false, and that may have led to the current mess of software design: 1- That OO decomposition and design reflects "how we perceive the world". 2- That if there is merit to this notion that we naturally decompose/understand the world in terms of objects, has-a and is-a, that this maps to the set of formalisms we usually call OO Design & Programming. For example, it's obvious to me "message passing" is a (sometimes useful) abstraction completely alien to how we perceive the world -- nobody thinks in terms of sending messages to the objects. A message is something you send to another sentient being, preferably a person.
- seanmcdirmid 12y ago> I agree about naming things, but I don't consider it an OO exclusive. It is not an OO exclusive but it is at the foundation of OO (naming things, relating them, and such). Math (outside of maybe graph theory) is the exact opposite (names lie about fundamental truth, let's work with truth); that is the direction that pure functional programming really takes (many reduce it to immutability, but that is just a consequence of not having identity and aliases). > 1- That OO decomposition and design reflects "how we perceive the world". That is not really how its pushed. It is more about how we "talk" about the world. After all, humans are writing the programs, probably together, talking to stake holders and all. OO is not the best way to talk to the computer, but it might be the best way to talk to humans about programs until we can evolve into more Vulcan like creatures. Also, are evolved capability for language has greatly influenced how we think and solve problems. People find it easy to talk about things, ascribe names to them, and arbitrarily relate them to other things. This really gets in the way of learning math for most people (names and ascribed relations are informal, they can lie), but it is very accessible to deal with the computer world like you would the physical world (even if it is often technically wrong). > For example, it's obvious to me "message passing" is a (sometimes useful) abstraction completely alien to how we perceive the world -- nobody thinks in terms of sending messages to the objects. A message is something you send to another sentient being, preferably a person. Many people anthropomorphize their objects, which again is naturally human; again, objects are not for computers, they are for people.
- dragonwriter 12y ago> Many have come to believe this, but is it true? I don't perceive all of reality as a strict hierarchy of things constructed out of templates, with "is a" and "has a" relationships between them. The strict hierarchy thing is more a feature of the class-oriented programming of C++ and its close relatives (an artifact of what made sense when approximating OOP in something merged into the existing syntax and type system of C) than it is inherent to OOP. Unfortunately, the popularity of C++ and Java means that its hard to tell when people are talking about "Object Oriented" if they mean it in the general sense that existed prior to those languages which they are one of many approaches to facilitating, or if it means it in the narrow, static-class-oriented sense which evolved out of the particular approaches of those languages and their close relatives, which is something different.
- discreteevent 12y agoAt a high level a lot of systems are made of some kind of objects or actors. Things that transmit and receive signals or messages. Telecoms, the internet, Unix processes, micro services, smoke signals, human conversation and instruction. So maybe you can do pure functional down below but at a high level your system will always have some kind of objects (for want of a better word). Plus at a high level these systems don't share state. They can only do so via signals. This turns out to be a good enough solution for concurrency. People can demonstrate how you can still get deadlocks etc but if the granularity is large enough this rarely turns out to be a problem in practice.
- tolmasky 12y agoI kind of stopped understanding this explanation of inheritance (when I switched to composition of course ;) ). When I look at the world I see a universe of composition, we get molecules by composing atoms, atoms from composing protons, neutrons, etc. little "modules" that come together. More practically, everyday objects seem to follow this too. A motorcycle and car aren't both "refined" from some "wheeled vehicle" that car companies buy, rather they share some similar components that car companies buy. Their similarities are thus either emergent (interface/protocol) or a direct result of their shared components. In unity for example, you don't inherit every hit testable thing from some class (like you would in cocoa), but rather attach a collider to things that can be hit tested. The collider actually worries about one concern, and you can therefor conveniently attach multiple colliders to one object for example (makes it easy to form complex hit regions). Similarly things that are visible have renderers attached. This matches my intuition of working with real objects, where is buy parts and put them together, not think to myself which "master" part my new thing derives from, purchase it, then rip it open and start modifying it as step 1.
- ww520 12y agoOOP is not that slow in a typical application. Pure functional programming's emphasis on copying everything causing excessive garbage has an impact on performance, too. OOP's problem with performance is that it doesn't play nice with modern CPU's L1/L2 cache since most typical OOP implementations don't allocate objects in contiguous memory. But only a very narrow set of problem niches such as high data volume high performance simulation require packing data tightly to take advantage of L1/L2 cache. The hundreds or thousands of entities using OOP in a game won't cause a sweat. The cache problem would have an impact when there are hundreds of thousands or millions of entities and you need to process them 60 times per second. The L1/L2 cache performance hit can exist in functional program, too. The list and it cells in LISP are not allocated contiguously. Basically any non-array data structures would not play nice with cache. That just means to use the right data structures for the right job. For high data volume and high performance processing, use array. Whether it's used in the context of OOP is irrelevant.
- deleted 12y ago[deleted]
- mkehrt 12y agoUgh, pure functional programming language implementations don't generally actually copy everything. Because data structures are often immutable, pointers can be used rather than copying. Just a minor nit.
- jameshart 12y agoYou're assuming that an object is always going to be modelled as a heap-allocated struct-like block of memory containing all of its state. There's no reason that has to be the case, though. You could create a system with an object-oriented programming model whose data storage was array-backed/table-oriented. You could probably even code something like that up in a fully GC'd OO language like C#. The way Java and C# manage string intern tables is an example of the kind of specialized data backing that's possible behind externally OO interfaces.
- Chris_Newton 12y agoThank you for raising this. All too often debates about OOP get hung up on the overuse of inheritance. That’s a fair point, to be sure, but IMHO there are two much more fundamental limitations that are inherent in the OOP way of doing things. 1. In OOP, one object is special. Whether it’s the object that is receiving a message or the object that gets a special designation like `this` or `self`, something is always given a special emphasis in a purely OOP design. However, many useful algorithms take multiple inputs where none needs to be singled out in that way. Where do such algorithms live? Trivial example: A symmetric binary operator like +. Grown-up example: You don’t really model funds transfers by sending a `debit` message to one instance of a `BankAccount` class and a `credit` message to another instance. 2. In OOP, one object is special. This is a generalisation of the point that zmb_ made. Usually in programming we don’t work only with single, self-contained data points. Most interesting things happen when we manipulate structured data and consider relationships between data points. In purely OOP designs, we often see classes representing single data points, and then further classes to represent containers of that type. However, if the implementation of each data point is locked up behind the interface to a particular class, and then we have to access the points in each container through the container’s own generic interface, we are constrained in the access patterns we can use. Emphasizing individual, self-contained data points is often the wrong level of granularity for promoting either code reuse or efficient designs. Example 1: What if we want to implement a more efficient representation of a data set that supports a different access pattern, such as the kind of continuous memory case zmb_ mentioned? Example 2: What if we want to enforce constraints on a whole set of data, or model relationships between structured data of different types? In each case, it may be very difficult to reuse existing algorithms that are locked up in methods on existing single-data-point classes. We might want to store the underlying data in a different format, and converting between formats just to access functionality artificially tied to a specific variation is likely to be awkward and inefficient. If we instead build our modules as a set of fundamental data types and a set of accompanying algorithms — which could be as simple as a library of C structs/enums and functions using them — then the artificial barrier doesn’t arise. We can still present a clean interface/implementation for each module as a whole, but we aren’t forcing the implementation details to be separated just because Everything Must Be A Class.
- 12y ago