
Why Is Object-Oriented Programming Useful? With a Role-Playing Game Example - AlSweigart
http://inventwithpython.com/blog/2014/12/02/why-is-object-oriented-programming-useful-with-an-role-playing-game-example/
======
woah
Since this is aimed at new developers, I would encourage anyone who falls into
that category and is reading this to also look at alternate programming
methodologies. The key component of OOP is the mixture of code and data into
"objects". This can be very useful for physical simulations such as games,
etc., since the real physical world is actually made of objects.

However, many feel that applying OOP to software that is not for physical
simulation has led to a huge amount of wasted effort over the previous
decades. The problem is with code reuse. The only real form of code reuse that
OOP addresses is direct object inheritance. If you want to reuse a piece of
code, the way to do it is to make an object that is a 'subclass' of an object
that has the code that you want to use. The actual relationship of these
objects is often not that simple, and people often create baroque inheritance
trees to force their logic into the OOP pattern. Worse, is that OOP is built
into many languages, which leaves you no choice.

An alternate pattern is functional programming, where a program is modeled as
a series of data transformations, instead of a universe of interacting
'objects'. To write the code, one looks at what data will go into the
software, and what data will need to come out. After all, if the right data is
coming out of the program at the right times, it works.

Instead of breaking code into objects, you write functions that process the
data correctly, and assemble them into larger structures, often resembling
pipelines. The nice thing is that a lot of these functions are very reusable
across projects and within a project. Avoiding the messy mixture of code and
data allows you to identify the common patterns in your code, and refactor and
reuse more easily.

EDIT: Here are a couple of libraries which have really facilitated functional
programming for me in node.js

\-
[http://ramda.github.io/ramdocs/docs/](http://ramda.github.io/ramdocs/docs/)

\- [https://github.com/dominictarr/pull-
stream](https://github.com/dominictarr/pull-stream)

~~~
zmb_
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
It'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
> 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
I'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
Inheritance 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
Sure, 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.

~~~
jakevn
You have to draw the line somewhere. The structure of a typical OOP language
program such Go, Rust, Java, C++, C#, etc. are not comparable to that of a
typical Haskell program.

All of the languages that are considered OO rely heavily on the binding of
data and functions into objects.

Others will have deeper ideas of what an OO language needs to have, but the
basic definition is about objects. That is, the binding of data and functions
into an object.

In modern OOP discussion, it seems many already have come to the conclusion
that inheritance is something to be used very sparingly, and can be done away
with in favor of composition in most use cases.

~~~
tolmasky
I agree that you have to draw the line somewhere, but I believe the is-a/has-a
dichotomy more accurately separates the different modes of thinking. To me the
structure of a Go program is completely indistinguishable from one in C++
(precisely because of the lack of inheritance). When I look at the design of a
C++ program (or say Obj-C), its all about class hierarchies. The docs are all
about the class diagrams, step 1 of most those programs is usually "subclass
___". It immediately drops you into that view of the world, and I believe that
with that view comes the guiding hand of your program's design.

Compare that to Go, which focuses on the _traits_ of objects instead of their
incidental ancestry. In go you'd define a function or method applying to an
abstract interface that has certain properties. For example, I would say "give
me an object that has a show method", not "give me something inheriting from
Printable". This is completely analogous to the very abstract typeclass-style
programming you do in Haskell ("give me something that derives Show"). Haskell
object architecture is all about defining an abstract typeclass and reasoning
about what you can do given these existing methods. You then supply an
implementation that fits the type class definition, exactly the same abstract
analysis of fundamental properties divorced from their specific owners as
inheritance-less interface/protocol programming in a language like Go.

Again, there is quite _literally_ no difference in "binding" a function to an
object through the dot syntax vs through type. If in haskell you say the meow
function applies to the Cat data type its not any different than having a
meow() method on a Cat in C++, neither can call that on anything else, its
quite bound.

------
jameskilton
Unfortunately for the OP, when it comes to game development, it's widely
understood that OOP only ends up painting the developer in a corner
eventually. Yes using Objects, Inheritance, and Polymorphism works well for a
long time, but the more objects and deeper the inheritance tree grows, the
more inflexible the system becomes. You also run into problems with reality.
When the code hot path calls across multiple object types and a invokes a
bunch of polymorphism every frame, the code is going to be slow and there's no
obvious bottleneck. The code is slow because the CPU is constantly waiting for
its cpu and data cache to catch up.

Game development today is moving quickly towards components, composition over
inheritance, and Data Driven Design techniques. Check out the myriad of posts
on Entity/Component systems that have sprung up over the past 10 years. It can
be harder to grasp at first but has huge wins over the traditional OOP model.

~~~
adnzzzzZ
This "huge win" depends heavily on what you're making. If you're making simple
games where performance is not that much of a problem (like most indie games)
then moving to entity/component systems makes no sense and tends to make
everything more complex than it needs to be. A good middle ground in those
cases is to just stick with normal OOP with shallow trees (no more than 2-3
levels) and favor simpler types of composition like mixins whenever needed.

~~~
rrradical
As someone who has made indie games with both traditional OOP and component
systems-- I vastly prefer component systems. I disagree that they make things
more complex. I found that they made things much simpler. Although, it does
require learning about and possibly developing a component system.

If you are talking about very very simple games, it would perhaps not be worth
the effort, but at that level basically any architecture will work.

~~~
seanmcdirmid
Entity component systems are just another form of OO that rely heavily on
ontology and named object instances. Unless you mean OOP must be Java, then of
course, it's not.

------
lmm
I feel like a lot of the problems OOP solves are working around inadequate
type systems. If you can make a distinction between Int @@ CharacterHP and Int
@@ WeaponDamage such that you simply _can 't_ subtract one from the other
without going via the appropriate function, then the argument for having
private member variables and object methods to mutate them (which is the
controversial part of OOP, I think everyone agrees with having data structures
and some form of polymorphism) goes away, doesn't it?

~~~
humanrebar
Right, but you should also include notions of immutability and ownership in
your explanation. OOP is partly about managing complexity. If the mutation and
sharing in your data is managed by the type system, private data is much less
important.

That being said, OOP is also about managing interfaces between the different
parts of your code. Adding a little more type info to your variables doesn't
help you if you need to drastically change how a variable is accessed and
mutated (memoized, retrieved from a service, persistence layer, etc.) while
avoiding a running a giant find/replace across your entire project.

~~~
lmm
> Adding a little more type info to your variables doesn't help you if you
> need to drastically change how a variable is accessed and mutated (memoized,
> retrieved from a service, persistence layer, etc.) while avoiding a running
> a giant find/replace across your entire project.

I feel like if you have a generic "context"-like notion then you can have a
lot of this pass directly through. E.g. I recently changed where a Scala
report gets most of its data, making it use an (async) web service rather than
a database call, and I only really had to change the "top and bottom" \- all
the intermediate code handles any kind of context as a generic "F[_]",
sometimes requiring particular typeclasses (Applicative, Comonad) depending on
what it does with it.

------
bsder
The problem is that object oriented programming is a premature optimization
that has outlived it's usefulness.

Object oriented programming _enshrines_ mutation.

Mutation is a good optimization when memory is expensive and fast relative to
computation. Memory is now cheap and slow relative to computation. You can
execute 100's to 1000's of operations in the time it takes to chase a pointer
that is not cached.

~~~
Gurkenmaster
If OOP encourages mutation can you explain to me why the String class in java
is immutable?

~~~
bsder
It's a holdover from when Java was supposed to be a language for small
machines.

However, if you're being snide, let me retort "So why does everybody in Java
immediately recommend that you use the StringBuilder class instead of String?"

------
Kiro
So yeah, in games OOP makes perfect sense but I have a hard time applying it
in other code. I mostly build applications that make some API calls, save the
result to a database etc. What is an object in that context?

The same goes for the classical Car and Animal OOP examples. Yeah, that makes
sense but how often do you have such entities in a real code base?

~~~
Puts
The problem with object-oriented programing is that it makes objects the most
important abstraction, which it's not. I'd say states are the most important
abstraction of a program.

I think I've grown so much as a programmer since I started thinking more about
states and less about objects.

------
rezistik
I was talking to a friend who started doing web development transitioning from
game development. I came from a JavaScript background heavily based around
Lodash and 'nearly' functional paradigm. I was discussing my interest in more
purely functional languages like Haskell and Clojure. Kind of rattled of my
distaste for OO programming and how it just doesn't make sense to me, it seems
like a lot of fluff and boiler plate when our most common use case is moving
data from a form to a data store.

He made a lot of good points that with game development OO makes a ton of
sense. I kind of agree. OO works amazingly with game development but with
application development I'm really finding the functional paradigm so much
more logical.

~~~
vespakoen
Functional programming in JS is possible, ramda / wu (others?) make it very
easy to get started, of course it is not as great as with functional
programming languages, but still, it allows you to do pretty cool stuff, it's
like lodash but with the callback moved to the first argument, turning
something like this:

var isMultilanguage = function(field) {

    
    
      return field.multilanguage === true;
    

};

var isNotMultilanguage = function(field) {

    
    
      return field.multilanguage !== true;
    

};

var getMultilanguageFields = function (fields) {

    
    
      return _.filter(fields, isMultilanguage); 
    

};

var getNonMultilanguageFields = function (fields) {

    
    
      return _.filter(fields, isNotMultilanguage); 
    

};

into this

var isMultilanguage = R.where({multilanguage: true});

var isNotMultilanguage = R.not(isMultilanguage);

var getMultilanguageFields = R.filter(isMultilanguage);

var getNonMultilanguageFields = R.filter(isNotMultilanguage);

[https://github.com/ramda/ramda](https://github.com/ramda/ramda)

[https://github.com/fitzgen/wu.js](https://github.com/fitzgen/wu.js)

~~~
jdd
The current stable version of Lo-Dash supports _.curry. In 3.0 Lo-Dash will
add support _.rearg, _.ary, & _.curryRight. Using a combo of them you can
easily create auto-curried functions.

    
    
      var where = _.curry(_.rearg(_.where,1,0));
      var isMultilang = where({multilanguage: true});

------
lsiebert
The weird thing is, many if not all of the arguments apply to C with
structures, unions, and function pointers... and I don't know that consider
that properly object oriented.

------
cportela
<unrelated> Al, could you made the fonts on your site bigger, make styling via
css not on each element and introduce some nicer Google fonts? Also, if you
could use `<code>` and `<pre>` tags that'd be a solution where you could
ignore the rest of what I said.

Reason being that I save these articles for later to show people who I try to
teach programming and my attempts to save this page were unsuccessful.

------
mrev19
Wow I haven't seen that character record sheet in 30 years. So mewhere I have
a 10th level elf fighter with 18's for every trait (17 for charisma just to
break it up.)

Translation for non-D&Ders - I was so nerdy I made my own characters for my
own games I was DM for with completely invented characteristics rather than
rolling dice for traits like the rules say

------
Tehnix
The author should really consider some proper way of displaying code, instead
of a textbox.

Also, for some reason, on safari the code is in one line, while on chrome it's
in multiple lines, like it should be.

~~~
DEinspanjer
The highest priority rule is the in-line which specifies white-space: nowrap;
which doesn't preserve multiple whitespace. Firefox and Safari does what he
says. I'm not quite sure why Chrome is displaying it differently..

