4 ms·
While I'm sure the topic (and title) may be seen as hostile, this is a very nice code walkthrough and discussion. The arguments against OOP seem to be 1) OOP o
by pdkl95 11y ago
While I'm sure the topic (and title) may be seen as hostile, this is a very nice code walkthrough and discussion.
The arguments against OOP seem to be 1) OOP obscures data structures by hiding them in too many data types, and 2) the boundaries created by these data types forces workarounds like extra fields to hold a root structure pointer even though it isn't part of the type.
OOP - in practice - does tend to encourage far too many types. A lot of these types are often represent code or design decisions, not the actual data. Instead of a "few" structs that clearly show how the data is organized, we tend to see OOP code that has the data structure spread out over various classes, interleaved with types that actually represent different actions (with the same data).
The solution I always try to keep in mind is that OOP can be useful if-and-only-if the data actually has a hierarchy. A single standalone class is equivalent to a basic struct passed to functions. The class only becomes worthwhile when are able to use polymorphism that closely model the data. The serious problems start when you try to force everything into a class; in extreme cases you end up with the Kingdom of Nouns[1]
The second argument seems to follow from the first; if you have too many types, you tend to take shortcuts like keeping a local reference to a parent or global object that is really outside your scope. This tight coupling creates maintenance problems and further confuses what the type is actually representing.
[1] http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom...
- sz4kerto 11y ago> A single standalone class is equivalent to a basic struct passed to functions. Not exactly -- a struct is equivalent to a class with public fields. If you have a non-anemic model class then you can guarantee that the object's state is within some boundaries (defined in the mutator methods).
- stcredzero 11y agoThe solution I always try to keep in mind is that OOP can be useful if-and-only-if the data actually has a hierarchy...The class only becomes worthwhile when are able to use polymorphism that closely model the data. You don't need a hierarchy to have polymorphism. The Observer pattern is very useful in my space game in Go. I wrote a small pair of Interfaces which makes it very easy for certain objects to register to receive notification when a ship despawns. type DespawnListener interface { NotifyDespawnShip(Ship) } type DespawnNotifier interface { SetDespawner(Ship) AddDespawnListener(DespawnListener) NotifyDespawn() } type basicDespawnNotifier struct { ship Ship dependents map[DespawnListener]bool } func (self *basicDespawnNotifier) SetDespawner(ship Ship) { self.ship = ship } func (self *basicDespawnNotifier) AddDespawnListener(dsl DespawnListener) { self.dependents[dsl] = true } func (self *basicDespawnNotifier) NotifyDespawn() { for key, _ := range self.dependents { key.NotifyDespawnShip(self.ship) } } My framework sends NotifyDespawn() to any despawning ship. Now anything that has registered interest in that ship, like missile locks, tracking AI ships, or tracking missiles, will be notified when that ship despawns. Note that this makes the system extensible. Right now I have each Ship entity tracking dependents in its own map, which it can do by composing a basicDespawnNotifier into its struct. Later, I will move the implementation guts into the object that implements the game loop/instance, which will eliminate the map from despawning entities, reducing GC pressure, and allowing me to cut-off circular dependencies. Note that all of the game code using the two interfaces won't change at all. No hierarchy is necessary in Golang or Smalltalk, because both have implicit interfaces and allow composition. (Golang is nice because its implicit Interfaces are made explicit and typesafe.) The op is somewhat misguided. Good OO is comprised of good design, just as good computer games are comprised of good design. Expecting good OO of any programmer who just knows the "rules" of OO is like expecting the output of all game jam hackathons to be good games. Of course half of all OO projects found on Github with "obligatory" OO are going to show below average design, just as not every game from a game jam is not necessarily a masterpiece.
- sklogic 11y agoThere is no such a thing as a "good OO". You don't need any OO for your example. Polymorphism is not an OO.
- stcredzero 11y agoYou can have good OO with just encapsulation and polymorphism.
- marcus_holmes 11y agoI think you've just reinforced op's point instead of showing how he was misguided. Saying "Good OO is comprised of good design" is meaningless - good code regardless of methodology is comprised of good design. You can't have good code unless it's been designed well. Good design is not a feature of the OO methodology, they are two independent properties of a program (design quality and methodology). "Expecting good OO of a programmer who just knows the rules of OO" is exactly what OO was sold to us as achieving. By following the rules of OO design we were supposed to be able to automatically have better programs that were less likely to end up as spaghetti. If we wrote lots of small classes that just did one thing well and encapsulated their state properly we would magically have programs that were easy to understand and maintain, and we would be able to re-use the classes in other programs! This was hardly ever achieved by anyone, and if it was it was independently of OO methodology not due to it. As you point out, reinforcing the op's points, it's very easy to get OO very wrong, even for experts.
- stcredzero 11y agoI think you've just reinforced op's point instead of showing how he was misguided. I can change the underlying implementation of this subsystem, giving it additional capabilities, modulo some find-erase and compiler-guided changes, which I can be 100% sure are done correctly. Please give your own example of code which has the above properties. "Expecting good OO of a programmer who just knows the rules of OO" is exactly what OO was sold to us as achieving. As someone who was around for all of that rigamarole since the 90's, I am making precisely the point that this is wrong. The same goes for functional programming as well. The same goes for playing music or designing the inside of your house. There's a little practice, iteration, and self criticism that has to happen. Otherwise, what you get is pretty mediocre.
- norea-armozel 11y agoI've been running into the problem of too many datatypes in a project lately. Right now, I'm stripping down what I can to the bare essentials and not even for optimization/speed, just for clarity since initially the project I was given had lots of odd recursion, guess-work methods, and the like. My partner on the project formalized some of it as classes. Now we're finding we can remove a good chunk of what we wrote for just better algorithms (less dependence on entity framework and the like).
- rbehrends 11y ago> OOP - in practice - does tend to encourage far too many types. Keep in mind that the concept of a class unifies the concepts of modules and types. A class can be seen both as a module (with late binding, and of which there can be several instances) and as a record (with methods to abstract over the representation and allow polymorphism). The advantage is that you don't need a second module language on top of the type system. Consider, for example, that as of OCaml 4 - with first-class modules and generative functors –, OCaml's module language is essentially a second fully-featured OO system. Using it as such would be awkward because of all the boilerplate involved (it isn't meant to be used that way), but all the functionality is there. This is basically because first-class modules provide late binding and generative functors allow for multiple instances of the same module. But this means that, yes, you get more types because some of these types are actually modules in disguise. > The solution I always try to keep in mind is that OOP can be useful if-and-only-if the data actually has a hierarchy. A single standalone class is equivalent to a basic struct passed to functions. Not really. A basic struct does not have methods that abstract over the representation and does not support polymorphism. Also, it avoids duplicating language functionality for concepts with overlapping, but not identical functionality. Note also that structural subtyping does not require a hierarchy.