Object Oriented Debate Part 3: Damned if you do…

By Shamus Posted Tuesday Dec 20, 2016

Filed under: Programming 78 comments

So the original problem is that programs turned into an incomprehensible mess. Too many disparate systems can make arbitrary changes to the state of the problem at any time.

The solution we’ve been using (or trying to use) for the last couple of decades is to stick the various bits of our program into appropriately named objects, and then fit those objects into a hierarchy.

If I have a SpaceMarine object then I should have a limited number of things that I can do to it. Perhaps Heal(), Damage(), or Spawn(), and so the inner workings of a SpaceMarine will be hidden from the outside world. This is called encapsulation. It keeps the complexity of the SpaceMarine from leaking out into the rest of the code, and it keeps the complexity of the rest of the code from polluting the SpaceMarine.

Now let’s discuss some of the objections to OOP. Remember that this particular conversation was started by Brian Will in his video:


Link (YouTube)

But I don’t want to simply repeat what Will says, so I’m going to offer my own criticism of OOP. If you want to hear his arguments, watch the video.

Problem One: Complete encapsulation is an unattainable ideal.

In my indie game Good Robot, enemy Robots can act upon each other. A robot might create other robots. It will both shove and be shoved by other robots that are crowding it. Robots can also create bullets. Robots can damage the player through direct contact.

BulletsIn this context, a “bullet” is any damaging projectile: Missiles, lasers, bombs, shotgun pellets, etc. can destroy robots. Bullets can destroy each other when lasers shoot down missiles. Bullets can change their trajectory (homing missiles) based on the presence of robots. Bullets die when they strike doors, but they might also cause the door to open. Bullets can destroy machines that produce robots.

Doors can act on robots and the player, preventing them from passing through. They block bullets. They open in response to player proximity.

The player can create bullets. The player can be killed by bullets. The player alters the behavior of robots. The player alters the course of bullets. (Homing missiles again.) The player alters the behavior of doors. (It opens if they’re close enough.)

Here are some bullets interacting with each other.
Here are some bullets interacting with each other.

Keep in mind this is a simplified explanation of an already simple game. Once you’re talking about a game where players can control vehicles that have mounted weapons that can shoot projectiles that can destroy different vehicles that can cause explosions that will detonate barrels that can collapse physics objects that can crush players, you’re up against some serious challenges in enforcing simplicity on a system that’s inherently complex.

Where is the encapsulation here? You can’t just shove bullets into a box and forget about them, because when bullets are updated they need to change the state of almost everything else in the gameworld. That’s the whole point of bullets. When you update bullets, a bunch of non-bullet things need to change, all across the program. You can fight this truth all you want, but in the end you haven’t really solved the original problem with procedural programming, which is that state interactions are complex and hard to understand. You’ve still got all of these interconnected systems changing one another at different times. You can’t hide their state away. Having objects A B and C all able to make changes to each other at various times is the goal of the program and the reason the problem was so hard to solve in the first place.

A cheeky programmer might say something like, “Hey, robots and bullets are both just “things that move around and run into each other”. So let’s make them two different flavors of the same shared idea. Call them uh… “flyers”. Then it’s just Flyers acting on other Flyers, and that technically doesn’t break encapsulation!

But this is a semantic trick that doesn’t actually do anything to help us solve the problem. The program is exactly as complex as it was before. More importantly, bullets and robots are very different things and need to be handled in very different ways. Bullets are tiny, number in the hundreds, and live only for a few seconds. Robots number in the dozens and live for many seconds or even a few minutes. Both of these things need to be kept in separate lists, and those lists should be tightly packed in memory. They need to be drawn at different times in the render loop and they serve totally different purposes in the game.

By mashing these two different ideas together, all you’ve done is made the nomenclature confusing and created a bunch of performance concerns.

2. Object hierarchies just replace one complexity with another.

Not to scale.
Not to scale.

So next our enterprising coder will try a new approach. The point of OOP is to prevent different parts of the program from altering each other’s state in unpredictable ways. So what if we make a new object? We can call it “Scene” or something. It’s in charge of all the bullets, robots, doors, the player and anything else that might be doing rude things like changing the state of the game. Since the scene object owns all bullets and all robots, they are now part of its overall state. It’s not robots and bullets changing each other, it’s the Scene changing itself. And that adheres to OOP principles! Hooray!

Of course, this solution is even worse than the last one. We’ve taken the code that controls the behavior of bullets and robots and pulled it out of their respective systems and into this huge container looming over them. Now if I want to see how bullets work I might need to look in Bullets.cpp, but maybe what I’m looking for is in Scene.cpp.

The scene class will continue to bloat until it reaches gigantic proportions, because regardless of what you call it, conceptually this class is the class for “anything that might change the state of the game”. Sooner or later everything will end up inside of this thing and you’ll end up with all game logic controlled by a single class. You’ll have all the complexity problems of PP, plus you’ve got terribly organized code.

Alternatively, the coder might create more and more classes for wrangling all of these types. A class to own bullets and robots. Another for doors and machines. Another for the static scenery. And then another class to manage all of those. All of those inter-object relationships can be mediated by these high-level abstract classes that only exist to allow these objects to talk to each other, rather than letting them simply talk to each other directly. As if making them talk through this third party will somehow simplify the inherent complexity of the problem. It’s an attempt to fix the problem through bureaucracy.

These are just a few example. There are endless ways to impose OOP principles onto this problem. A programmer sees a problem where it’s really hard to keep track of state changes, and take it as a personal challenge to create some “pure” (yet impractical) OOP framework around it. The problem is that we eventually lose sight of the goal, which is to create clear, efficient code.

There’s nothing wrong with OOP, you’re just bad at it!

Have you tried trying harder?
Have you tried trying harder?

When you point out that OOP results in a bunch of abstract classes, or that functionality ends up moved to non-intuitive places simply to satisfy OOP orthodoxy, the usual retort is that this won’t happen if you would just do it “right”. And here we come back to the problem of talking about coding as if it was a single profession and not a bunch of vastly different jobs that all share the same basic tools. An aerospace mechanic and an HVAC mechanic might both work on “machines” and they might both use wrenches, but neither one of them is qualified to tell the other how to do their job.

Insisting that OOP works fine in all cases is like insisting that all machines can be fixed with a wrench because you’ve never encountered a problem that couldn’t be fixed with a wrench. It’s basically claiming that no problem could possibly exist that might be ill-suited to a strict OOP design. Instead the OOP apologist will insist that your problem – which they’ve never seen and know nothing about – could be solved by simply following their advice with the proper level of tenacity and blind faith.

My problem isn’t really with OOP itself, but rather with the idea that its principles should be adhered to at all times without asking if it’s the right tool for the job. Clearly some problems lend themselves to one programming language better than another, and it’s reasonable to think that the same can be said for programming paradigms. It’s even reasonable to assume that in a large project, different parts warrant different approaches.

The Mountain Before Us

I just noticed that the specs say the goal is to climb the mountain, but it never says how high.
I just noticed that the specs say the goal is to climb the mountain, but it never says how high.

Let’s imagine an expedition tasked with climbing a mountain.

Alan says, “Let’s go up this steep slope to reach the top.”

Barbara replies, “That would be idiotic. That slope is too steep and dangerous. Half of us would die of exhaustion or injury before we made it.”

Alan says, “Nonsense! Any slope can be overcome with sufficient physical training.”

Barbara points to the road that winds back and forth up the side of the mountain. “We should go this way. The gradual slope will be far easier to overcome.”

Alan shakes his head. “That road is really long. We don’t have enough food to make it.”

“No! Any distance can be overcome if you just adhere to a strict rationing program.”

And so these two dunces gainsay each other until they get frustrated and set off in different directions.

Eventually, someone from Alan’s party staggers back and reports that the direct assault was a disaster. From now on, he’s a “road-and-rationing” man all the way.

Meanwhile, someone like Brian Will crawls back to the base camp to report that they followed the all best practices for rationing and it was still horrific. People starved. And so he swears he’s never taking the path again.

People arguing over which way is “right” are missing the key truth that both ways are technically “right” in the sense that you’ll get there and yet “wrong” in the sense that you can’t get there without casualties. This is not an either / or question. It’s entirely possible that this mountain is simply too big to be overcome safely. There are limits to how much energy the human body can put out in a day and there are limits to how far rationing can take you, and this remains true no matter how vigorously you point out the flaws with the other person’s approach. Their entire argument is based on the faulty assumption that one of these two paths will lead to perfect success. It clearly isn’t the other person’s, so it must be mine!

Image taken from a documentary on <a href='https://en.wikipedia.org/wiki/Barkley_Marathons'>The Barkley Marathons</a>, one of the most savage physical races ever devised. Entrants must run five laps around the 20-mile course, which features rough terrain, no navigation aids, no aid stations, and requires thousands of feet of climbing and descent. There's a documentary about it on Netflix, which is pretty good.
Image taken from a documentary on The Barkley Marathons, one of the most savage physical races ever devised. Entrants must run five laps around the 20-mile course, which features rough terrain, no navigation aids, no aid stations, and requires thousands of feet of climbing and descent. There's a documentary about it on Netflix, which is pretty good.

We live in a world with finite money to spend on programmers with finite mental capacity to write code in a finite amount of time on hardware of finite power. Sometimes we run into problems that are really hard to solve. There will be challenges where the solution won’t fit neatly into theoretical systems designed by people who have never seen a problem like this before. Sometimes a problem might create unmanageable complexity regardless of what dogmas you cling to, because some problems are really complicated.

In fact, we’re doomed to run into hard problems, because there’s no upper limit on how much stuff we want to accomplish. If you doubled the brainpower of every programmer on earth, then a lot of our current challenges would indeed be reduced to “trivial”. But by next week we’d be up against a new bunch of problems that had previously been clearly beyond us and are now just barely within reach. And then we’d end up right back here, arguing about how to think about problems that are too complicated to be thought about and trying to manage all of this expanding complexity.

My point is that there are no simple answers or perfect techniques. Sometimes this job will be really hard and there’s nothing you can do about it. I’m okay with people like Brain Will who prefer a PP approach to authoring code. I’m okay with the folks who favor the OOP approach. What I’m wary of are the people who go around repeating dogma and insisting that all problems can be solved if you follow the One True Way.

 

Footnotes:

[1] In this context, a “bullet” is any damaging projectile: Missiles, lasers, bombs, shotgun pellets, etc.



From The Archives:
 

78 thoughts on “Object Oriented Debate Part 3: Damned if you do…

  1. Da Mage says:

    I like to approach my problems with layers.

    First using encapsulation and hierarchies as you said to separate things that can be abstracted apart.

    However, when things cannot be separated without causing lots of connection problems, then I move to patterns. These objects all share similar styles in order to make them interact using a consistent interface, even though they are different things.

    Lastly I just fall back to entwining objects. Take two things that need to know everything about each other, but are separate concepts (think an NPC object and it’s AI state), and just let to have full access and spaghetti code. I document what I can and just try and make sure the spaghetti is understandable.

    That’s just my approach though. I also often use a backward approach to building a system, get it working then repair the structure. Makes it much easier to design a structure when you have completed the code and understand it fully.

  2. Droid says:

    “In an alternate history, we could have had the same successes, if not even more success…”

    *shows US partitioned by Nazi Germany and Imperial Japan*

    That part of Brian Will’s video has some very unfortunate subtext.

    1. Echo Tango says:

      Link to time in the video? I don’t recall Brian talking about world politics, geography, or wars, but I was listening to it while playing a game, so maybe I missed it.

      1. Syal says:

        15:20, or a little after.

        1. Echo Tango says:

          Hah! I guess listening to the video while playing full-screen games misses out on a lot! That section of the video definitely seems like it’s made to start a flame-war on Java vs [other language here]. I guess it came from his experience of language-specific evangelization? Personally, I’d have left it out, to make the video as neutral and fact-based as possible, so I could focus on convincing people why my point of view was correct, without giving fuel for hostile argument. Oh well, it’s otherwise a very good video! :)

          1. BenD says:

            Sure, but the visual context is immediately accessible to a large subset of less technically-experienced viewers. It says: the alternative scenario could have all the same advantages at the detail/micro scale – or even better ones – but it may also have striking disadvantages (or at least problems) at the macro scale.

            1. Oliver Edleston says:

              Without having watched the video, the particular graphic mentioned sounds like a reference to The Man in the High Castle. That being a Philip K Dick novel exploring an alternate timeline where World War 2 ended very differently.

              This would explain the quoted line on “alternate history”, and would suggest the intent is more referential than it is flame-war bait.

  3. blue painted says:

    It’s worth remembering that the computer itself doesn’t have objects or messages and only just has functions and structures: it has instructions. Parallel and predictive but just instructions. All the high level structure is for programmers and other people.

    In my experience there comes a time when you just have to do the horrible/hacky thing to make it work: at times like this comments explaining *why* are really helpful for later! It doesn’t make much difference if you are going OOP or functional or procedural, the real world just isn’t tidy and neat.

    1. Echo Tango says:

      The comments also help if at some point in the future, you find youself with more knowledge about the hard problem, or how it fits into the overall picture of what your software is doing, since then you might have a less hacky way of solving the problem available to you.

    2. Daimbert says:

      Yeah. I tend to under comment, but one of the things I’ve really learned to do is add comments that say “You might think that I’d be doing X, but I can’t because of Y, so I’m doing Z”. Hopefully, when someone who has never seen the code comes along later at least they won’t have to wonder if doing X wouldn’t have been better …

      1. Blake says:

        Yeah I basically do this.
        Comment why you did things, not what you did.

        Sometimes you’ll have to write some really nasty or obfuscated code (SIMD, assembly, some c++ templates, etc) and it’s useful to have comments explaining what’s going on, but 95% of what you right should be easy enough to read what it’s doing, so you only add comments when you need to explain why.

      2. Tektotherriggen says:

        Yes! I hate reading terrible uncommented code by a previous programmer, because my choices are to assume either “This person was ignorant or stupid”, or “Maybe this was done for a very good reason”. This former makes me feel rude and arrogant; the latter makes me worry that I’ll be wasting my time trying to refactor it.

      3. Alex Broadhead says:

        +1

        We actually had a programmer in our office recently who subscribed to the ‘all comments are bad’ school of thought. (A.k.a. “My code is self-documenting!”)

        To me, this is inexplicable dogma. Sure, comments can be buggy, and yes, putting ‘// add a and b’ next to a line like ‘x = a + b;’ is stupid, but comments, deployed properly, are often the most important part of the code for maintainability. And you usually hope that your code will last long enough to be maintained…

  4. The first issue I see is the yellow diagram showing a hierarchy of objects. It is my view that #1 sin of how people teach OOP is focus on object hierarchy where a dog is a mammal is a animal. You are right when you point out that fitting things in a hierarchy is a problem.

    Where encapsulation works is not through a object hierarchy but through interfaces. The problem you describe is a classic example of the use of the Observer pattern. To recap a pattern is a description of how to create and put together objects in a useful way. Patterns are to OOP what algorithms are to functions.

    Design Patterns on Wikipedia
    The Observer Pattern

    When you read through the list of pattern you will note that most involve interfaces not a hierarchy of objects.

    From what you described in your post I would handle it as follows.

    Each object, including bullets, would implement ISceneObject.

    ISceneObject would include a type property so that if object needs to work with a specific type of object it can find out what it is and then cast it into a variable of the exact type instead of working through the ISceneObject interface

    The ISceneObject would also include a Update Method that is executed every time tick..

    The Scene Object would maintain a list of ISceneObject. One task it would do during every time tick is do some type of very quick culling procedure to check to see if one object can interact with another. The ISceneObject would expose whatever information needed to perform that check. Perhaps a rectangle of the bounding box surrounding the object. Given that interactions can increase by the square of the number of objects this a critical routine to get right. I am aware there are a number of techniques that are not just a simple traversals of lists.

    If a interaction is possible. Then you use the Interact method of the ISceneObject. The steps within the Scene are

    If on a gross level an interaction is possible between two objects Then
    ObjectA.InteractWith(ObjectB)
    End If

    The ISceneObject Interface would have a InteractWith method that accepts another ISceneObject. A Robot’s ISceneObject procedure would take the incoming Object check it type and hand it off to the right method within its class. If it a bullet it casts it to a bullet object and hands off to BulletInteraction procedure which determines whether it truly got shot and what damage was caused. Or perhaps the StaticObjectInteraction which would check to see if there is a collision between the Robot and a Rock. If either object is destroy then the Destroyed property of the ISceneObject is set to true and the Scene knows to clean the object out of it list.

    What this does is that each type of object defines for itself how it handles either other type of object. If you want to know what happens when a bullet interacts with a rock, robot, or another bullet you look in the bullet class. The same with a Robot.

    There may be further optimization possible given what I know about the details of the Good Robot program but I am just trying to stick to what you covered in the post.

    1. Draklaw says:

      A few remarks:

      “Patterns are to OOP what algorithms are to functions.”

      I disagree. Sometimes it’s true. But often, design patterns are a complicated way to do something that would be easy to do with simple procedural programming but in a more “object oriented” way. For instance, the visitor pattern that is just a convoluted way to make a switch, with no obvious advantages.

      Also, design patterns are not necessarily an OOP concept. There are a lot of patterns in others paradigms. The observer, for example, is easy to do with a list of function pointers. But yeah, people don’t like function pointers.

      I like to see OOP like a (bunch of) design pattern by itself. Seen like this, it makes OOP design patterns a strange beasts: patterns built on top of other patterns. We can go far down this road, but I don’t think there is much to win. (Abstractions over abstractions can be a good thing though.)

      “[OOP] most[ly] involve interfaces not a hierarchy of objects.”

      Yes. And no. To me, there is 3 stages to OOP: Objects (with encapsulation), Interfaces and Hierarchies. Each stage implies the previous ones. Different people put different things behind the term “OOP”. For example, in the video, Brian Will criticize only the first stage (Object + encapsulation).

      Importantly, each stage is more specific than the previous ones, and also enforce more structure to the code. It mean that when someone teach OOP by introducing hierarchies, he actually teach the thing that is the less often relevant. So clearly, interfaces are much more often the right tool to use. Actually, in plain C, the FILE type and the associated function form a generic interface that can manipulate different kind of objects (files, sockets, memory, …).

      Finally, I disagree that interfaces help structuring the code in a game like Good Robot. For instance, bullets deal damage to robots. Who should hold this logic ? Bullets or robots ? Somehow `bullet.interactsWith(robot)` should produce the same result than `robot.interactsWith(bullet)`. So both classes should know about each other, which doesn’t really fix the issue.

      I prefer the big Scene class here. A `scene.interacts(bullet, robot)` makes more sense: robot and bullets don’t have to know about each other. Scene need to know about everything though. But this is not necessarily an issue, you just need a lot of functions properly named and structured (yes, I know, this looks a lot like procedural programming).

      I lack experience in videogame development (because I lack time to do it…) but in my experiments, I like to have a global `update` method in Scene that call the main processing steps in a tick. Something like

      void update() {
      processInputs();
      movePlayer();
      moveRobots();
      moveBullets();
      CollisionPairs colls = detectCollisions();
      resolveInterpenetration(colls);
      dealPlayerRobotCollisionDamages(colls);
      dealBulletDamages(colls);
      }

      I find that it helps ordering the processing steps like this to think about how the game logic works. It’s much better than having a `update()` method in every game object with no control over the order in which they are called.

      Also, your solution is terrible performance-wise. Every call to `update()` or `interactWith()` has to check a vtable and potentially execute a different function on objects that are unlikely to be stored contiguously in memory. That will cause a massive amount of cache misses. With the above approach, objects can be grouped together and processed in batches. For instance, `dealBulletDamages(colls)` just go through the list of bullets in order. We can even separate all the collision-related data in a component so that `detectCollisions()` can go through the list of collision components efficiently. And you can try do perform some multi-threading to parallelize the update, but this is tricky.

      1. Daemian Lucifer says:

        “Who should hold this logic ?”

        Neither.You have an object bullets,an object robot,and an interface Interacts.The interface will connect the two together by passing info to the bullet to disappear,and by passing info to the robot to take damage.Neither object needs to be aware of what the other object does.

      2. To answer your points
        —————————————————-
        a switch is to execute one of several possible actions based on a condition.

        The observer pattern goal is to allow one object to tells a bunch of other objects that it state, status, whatever has changed. It up to each observing object to decide whether something needs to be done.
        —————————————————-
        Bullets vs. Robot
        Given the simplistic example it doesn’t matter which object holds the logic. You can have damage applied to the Robot via Robot.InteractsWith(Bullet) or Bullet.Interacts with(Robot). The only hard and fast rules I would give once you decide on an approach be consistent.

        Now given an entire system like Good Robot other considerations may make it better for the damage to occur via Robot.InteractsWith(Bullet) vs. Bullet.InteractsWith(Robot). Again once you decide which approach works for you be consistent.

        My personal preference is for the logic to be in the class being damaged hence Robot.InteractsWith(Bullet).
        —————————————
        Again I disagree, a scene should not have to know any thing about how bullets or robots interact. In addition scene.intersect(robot, bullet) would be a giant switch statement that would grow out of control as more object types are added. Also you will need a lot of internal guts of individual objects exposed which makes changing their details problematic.

        For example suppose I started out with Robot having a simple hit point system. And if a bullet intersect any part of the robot’s shape it does damage. Later I decide it would be better that individual parts of the robot could be destroyed. All the work would be focused within the Robot Object. The Scene object and the Bullet object remains untouched.

        For simplistic example where we just have a Scene, Robot, and Bullets my example is at best a wash, probably overkill. But when you add dozens of different robot, bullets, and other scene object it is a better method of organization. Plus the use of interfaces is largely self-documenting. So if you have to revisit that code months or year laters you can see quite clearly how that one section interfaces with the rest of the software.

        This is the problem of functional programming. It not explicitly clear which procedure works with which data. Classes and interface provide a guide to how the different parts of the system work together. And yes classes and interfaces can be done poorly.

        I had to maintain a well designed functional program for 15 years and a well designed OOP program for another 15 years of the two the OOP program was far easier to read and far easier to change without unintended side effects.
        —————————————

        When it comes to your example of handling update, it a wash compared to OOP because all it dealing with is players, robots, and bullets. Each new type of update will you to add another procedure to that list. With object and interface, the scene update procedure will be optimized early one and remain static.

        Sub update()
        processInputs()
        objectList.Update()
        End Sub

        (class ObjectList)
        Sub Update()
        Me.ProcessMoves()
        Me.ProcessInteractions()
        End Sub

        Sub ProcessMoves()
        Dim moveList as ObjectList = Me.CullObjectsForMove()
        For each tObject as IGameObject in moveList
        moveList.Move()
        Next tObject
        End Sub

        and so forth and so on.
        ————————————————
        Well there is performance and there is performance. For example the UI menus doesn’t have to be written in C. While the scene rendering needs to have every ounce of performance squeezed out of it. If it is that tight you setup a interface that has a method where you pass the needed data and command in one call and everything behind that interface is written to be as fast as possible in whatever form that needs to be.

        I have to deal with this due to the fact my software control industrial machines in real time. Some machine I can dump an entire list of shapes at once. Some I can only pass one shape at a time, and then there are the one ones where I have to pass each point back.

        So what I do is use something call the Strategy pattern.. They all implement the IMachineController interface. For the machine where I have to download information one point at a time while it running, the controller object sets up a C DLL and passes the shapes to it and it handles the download. After it completes then it returns that it finished and the rest of the code does what it normally does.

        Proper OOP programming it not about a slavish devotion to a concept. It a way of structuring the software so that each part only communicate thru explicit interfaces. And yes it imposes overhead that straight assembly and C programming doesn’t have. However OOP allows you to continue use assembly and C where it needed. Combined with the fact that CPU and graphic cards are vastly superior today, makes this a non-issue.

        If you look at Unity, CryEngine, Unreal, they are all capable of using C# where everything is an object and defined through interfaces. Behind the scene in C# the main subroutine is really just a static method of base object of the application.

        1. Phill says:

          You two might be talking about bullet/robot interactions at different levels though.

          Yes, once the bullet has hit the robot, it is most likely to be up to the robot code to determine how it reacts, although there had to be some info shared (the bullet has to be the source of the info about how much damage or does, or its momentum, or whatever parameters you are using in the damage system).

          The fundamental idea of OOP in these situations is that the bullet is the source of that data for anyone that is interested, while the robot reacts to that data without caring where it came from.

          Where Draklaw’s talking about a Scene class comes in for me is in determining whether a hit had taken place at all. You don’t want either class poking around with the internal positron and shape information of the other, really. You might well have an external physics manager class that handles that stuff and is necessarily more coupled to the implementations of the physical objects (or at least, some aspects of them like a physical avatar that each one can publish), and is where the information about how different objects interact is encoded (or you have a separate interaction manager for that) . Object type A hits object type B. Do they do damage, pass through each other, merge, bounce off harmlessly, one picks up the other?

          There are plenty of other ways of organising this stuff, but in my experience you often wind up with manager classes that have some implicit knowledge about what they are managing , rather than everything being absolutely encapsulated.

          1. Daemian Lucifer says:

            The manager,in this case,would only care about the relative position of the bullet and robot.Then,when it sees the two collided,it would send appropriate info to both.Then the bullet would respond according to its internal code for what to do when it hits something,and the robot would respond according to its internal code for what to do when it gets hit.

            1. WJS says:

              Correct me if I’m wrong, but wouldn’t the robot and the bullet both be running the same method? Bullet.hitSomething(Robot) and Robot.hitSomething(Bullet), rather than Robot.getHitBy(Bullet)? That way, your Scene object (or Director or whatever) would need to know which one to treat as hitting which, which would not be the case if it was symmetrical.

              1. Daemian Lucifer says:

                If you make robot.hitsomething,then you need to define different behaviors for when it hits a wall,a different robot,and a bullet.You dont have to do this if you have robot.gothit,however,because that one would be called only when the robot gets hit by something that can drain its health.I mean,the first one wouldnt be wrong,but unless you want everything to be dangerous to the robot,youd have to define doing nothing,which isnt required in the second one.

          2. You don't want either class poking around with the internal positron and shape information of the other, really.

            I disagree, the robot object is the one spot in the application that knows the most about how it shapes, and how it reacts to damage. To me it makes more sense to to have the robot object handle how it damaged from a given bullet. The bullet exposing everything that the robot (or any other object) need to use to determine what it does.

            What the scene should do is determine whether it initially worthwhile to check to see if a given bullet interact with a given robot. It holds the global state of the situation so it can readily determine that yes since the bullet is in the upper left quadrant and the robot is in the lower right quadrant don’t bother to go onto a more detailed evaluation of whether the bullets interacts with the robot.

            But if the scene did determine that a more detailed evaluation is need then you pass the bullet to the robot via interact with.

            Also I am aware that if there are hundreds or thousands of entities flying around, that you have to compromise the above in favor of performance. In which case I would do a prepossessing step and dump a list of objects/entities to a routine that would happen it as fast as possible.

            If it performance needs to be tighter than that. Then I would make the object a helper class rather than the primary source of data on the entity/object. You only instantiate a robot object from a memory handle when you need to do some non time critical task. When you are done with the operation you call a final method to write the altered state back to the main memory store via the handle.

            What I find from dealing with real time control of machinery, is that you retain a lot of the benefits of OOP even in a resource constrained system through careful design. I have stuff in assembly but it only the stuff that really needs to be in assembly.

            For example we have sensor that tell us if the machine is out of control or out of bounds where it is a threat to life and limb. The code that shuts down motions and safes the mechanisms resides in assembly. It needs to run as close to the source of the data as possible and operate as fast as possible.

            The code that tells the operator that something wrong and what is wrong is in the application written in C#/VB.NET. The time it takes to go over the link (serial, ethernet, etc) to retreve the status integer is considerable. But it doesn’t matter because the machine is now shut down. So the operator gets the status dialog in 300 millisecond instead of 50 milliseconds. It not going to matter.

            The same with a game. There are portions that are extremely time critical. So you wall them off begin a interface, write that section however you need it. And that interface will serve as documentation of exactly how it interacts with the rest of the system.

            1. Draklaw says:

              I think you misunderstood what I said about switch and observers. I was comparing switch and the *visitor* pattern, not the observer. The observer is actually quite useful, but don’t really need OOP.

              “Now given an entire system like Good Robot other considerations may make it better for the damage to occur via Robot.InteractsWith(Bullet) vs. Bullet.InteractsWith(Robot). Again once you decide which approach works for you be consistent.”

              That annoys me for two reasons. Firstly, if a new coder joins your team, he won’t know at first glance where the logic is, so he will have to search. It means that placing this code into objects don’t really help to structure the program. Well, I know the newcomers will have to learn the code base anyway, but my point is that it fells like you’re forcing the code into a class because that’s how things are done in OOP, not because it’s the logical way to structure this specific piece of code.

              Secondly, what if your bullet is actually a sticky bomb that stay stuck to the robot a few second before exploding, dealing area damages ? (And actually do the same if it hits a wall.) Should the Robot class knows about this ? If yes, you’re basically moving most of the logic I place in Scene into Robot, so I don’t see the benefit. If not, then what ? How does the robot know it should not deal with this and defer the interaction to the bullet ?

              For a less convoluted example, Good Robot has some bullets that are destroyed when they hit, other that goes through the enemies and maybe some that can go through up to n enemies. Should the robot really know about this and be responsible for destroying the bullet ? If the bullets destroy themselves, how to make sure they are destroyed after the robot took the damage ? You might want to defer the destruction of the bullets, but you still want to make sure some bullets do damage to exactly one robot. What if it intersects two robots in the same frame ? In the end, you will have a hard time finding a meaningful design if you insists on placing all the logic in Robot and Bullet, and you will likely end with tight coupling, which defeats the point of OOP.

              “Scene” might actually be a bad name, sorry. Let’s say that we have a class Scene that store the game objects in a structured way, and an other class `GameLogic` that deal with updating the Scene object. Now, the Scene don’t really know what are in the game objects (although you probably still need to group objects by type, so the Scene need to know this). The various game objects don’t need to know each other. They are much more structures than objects. You can have a few very simple methods like `isAlive()` that returns true if `hp > 0`, but no complicated logic here.

              The idea is that in a complex system like a game where a lot of different objects can interact in very different way with each other, the logic of the interactions should not be in the objects themselves because 1. we don’t know where to place code that by definition deal with several objects that _interact_ with each other and 2. this complicated logic will be scattered in a lot of different places and it will sooner or later be a nightmare to understand how objects interact (which is the whole point of game design).

              An other way to state this is that the core of a game is not the game objects but the interactions between them. So grouping the code by objects is the wrong way to structure the code. At the hearth should be a bunch of classes who deal with the interactions, and the GameLogic class I suggest is the one that manage all this. Scene and Game objects just store the data. You can think of them like a database.

              You say that my `Scene.interact()` method (let’s rename it `GameLogic.interact()`) will be a gigantic switch statement. But it’s not necessary. The whole point is to implement some logic to deal with the complex update procedure.

              Let’s follow your idea to have a method that deal with the interaction between two (or more ?) objects (note: I’m not sure it’s a good idea). We need to dynamically choose which function to call depending on the types of several objects. This is called multiple-dispatch. It turns out that OOP’s interfaces only do single-dispatch, so they are probably not the right tool for what we want to do. As I said earlier, interfaces are really just a design pattern, and it’s not the one we need here. Using the visitor pattern (which is a way to implement double-dispatch using interfaces) is not such a great idea because we loose a lot in terms of flexibility (again, the visitor pattern is no more flexible than a switch statement). So we will have to implement multiple-dispatch ourselves.

              Here is what I suggest. Each game object has a type (let’s say it’s defined by an enumeration, but if your language supports it, you can use reflection), in our case PLAYER, ROBOT, BULLET and DOOR. `GameLogic.interact(listOfObjects)` look at the type of the objects in parameter and sort them by type (the exact order do not matter, but it has to be well defined). For instance, `GameLogic.interact([bullet, robot])` will swap bullet and robot so that the order match the order of declaration in our enum. Now we can look in a hash-map if we have an interact function for the set `[ ROBOT, BULLET ]`. If yes, we call it with `[ robot, bullet ]` in parameter (we need to sort the list to make sure the parameters are in the order the function expects them). We can also have a list of functions for a given type set that are called in order (and we could use the return value to abort early if required).

              That’s it. We can now call `GameLogic.register(typeSet, function)` for each kind of interactions. And we need to write a function for each too. But now we are completely free to structure the code the way we want. For instance, we can have a file `movement.xxx` containing all the interactions related to movement, similarly for `damages.xxx` and so on. That’s much more logical in this case than to try to group things by type of objects. Movement and Damage can be classes if you want (and maybe store global variables like the default movement speed or the gravity). What I call functions can also be a class that implements a simple interface with a single function if desired (with the extra flexibility that you can add more function if ever required ?).

              Bonus point: we can batch objects by type. We take the list of interactions we need to process and sort it by type, then process it in order. Or even better, if the objects are stored by type in memory, we sort them by memory address. It will be much more cache friendly by reducing the amount of pointer chasing.

              Also, we can have a mechanism to tell in which order the interact functions are called, so we are sure that movement functions are called before those who deal with damages.

              I agree with you that not all of the code require the same level of optimization / abstraction. It’s fine do to most of the game logic in a high level, slow language (even Python can be used for this). This is nice for, say, the code that deal with homing missiles because you won’t have hundreds of them anyway. But you probably don’t want to do everything in a high level language. Collision detection is actually quite costly, and is typically an area where you want to make sure data is carefully layout in memory and avoid abstraction like interfaces to maximize performances (and maybe parallelize or move the code on the GPU). Sadly, most of the game objects will need to interact collision detection in some way. It mean that collision related code will likely be separated from the rest of the game objects.

              This lead to the idea that a game object is composed of different parts that deal with a specific aspect of the object: collision detection, rendering, physics, etc. This is the entity-component pattern which is extensively used in modern games. A benefit of composite game objects is that you can easily create new kind of objects by assembling different components. For example, you might have a 3DModel component and a Collision component. If you want to make a table, you will need both. For some light vegetation, you might use only the 3DModel component so the player is not stopped by it. And for an invisible wall that stops the player from falling into a pit, an object with a Collision component is enough. No need to write thousands of classes for each combination. Using this rather than interfaces makes the code simpler, more flexible and more efficient. Both Unity and UE4 use variants of this concept.

              This is related to data-oriented design, where the emphasis is placed on the data and how they are layout in memory rather than encapsulating data in objects accessed through a common interface. DOD is a different approach to software design that require to renounce some of the advantages of OOP for other advantages. As always, it’s a question of compromises.

              “What I find from dealing with real time control of machinery, is that you retain a lot of the benefits of OOP even in a resource constrained system through careful design.”

              The whole point of this post is that OOP is not always the best approach. There are different ways to structure your code, and in the case of videogames, trying to create game objects that encapsulate their state and interact with each other through interfaces actually lead to a sub-optimal architecture. By allowing yourself to not follow OOP principles, like “interfaces everywhere” or strong encapsulation, or even replace them with something more suited to your application like the multiple-dispatch implementation I suggest above, you can sometime do better. So the goal is not to “retain a lot of the benefits of OOP”, but actually have more benefits.

              And that’s the whole issue with how OOP is taught. In the end, students are no longer able to see alternatives, even if a clearly better alternative exists. I don’t say OOP is bad, but it’s just a tool and one size doesn’t fit all. And stacking design patterns to overcome limitations of design patterns lower in the stack, like the visitor pattern, is total nonsense.

              1. SAJ14SAJ says:

                I once read an article that looked at this issue of which object is responsible for holding the knowledge of how things interacted. As best I recall, the conclusion was that there could never be a complete analysis of which object is the right object. Instead, it reanalyzed the world so the controlling objects were the interaction rules themselves–the rules had their own independent implementation, and acted upon what were basically non-intelligent data structures for the actual game objects.

                I would love to read this again, and see how it informs modern expert system design and game design–but I have never succeeded in re-finding it.

  5. MichaelGC says:

    I googlimaged* that mountain picture because I thought it looked like Cumbria, in the north of England.

    It’s an extinct volcano in the Philippines. So, I was pretty close.

    Very much enjoyed this series, as a rank amateur! Or even an unranked one. Much of what you say towards the end has vastly wider applicability outside programming or even computers in general! But, you knew that. (And it is a small thing, but I’m not sure ‘Not to scale’ wasn’t a little bit of genius.)

    *Apologies.

  6. Duoae says:

    This was a great read. As a pet owner I specifically enjoyed the pet analogies. But I found a hole in your pennings where you missed out excel coding paradigms.

    Specifically the more useful of which, such as: the SUM function, remembering to use the ‘equals’ key to make equations work and appropriate use of the ‘merge and centre’ tool…

    This was obviously a poor attempt at a joke!

  7. George says:

    “People arguing over which way is “right” are missing the key truth that both ways are technically “right” in the sense that you'll get there and yet “wrong” in the sense that you can't get there without casualties. This is not an either / or question. It's entirely possible that this mountain is simply too big to be overcome safely. There are limits to how much energy the human body can put out in a day and there are limits to how far rationing can take you, and this remains true no matter how vigorously you point out the flaws with the other person's approach. Their entire argument is based on the faulty assumption that one of these two paths will lead to perfect success. It clearly isn't the other person's, so it must be mine!”

    I feel like this applies to so much more than just coding. Without wishing to start a conversation that would lead to a thread that must be closed, this totally is true of real-life arguments and debates of all kinds.

    1. Echo Tango says:

      You’re talking about Emacs vs Vim, right? Clearly the answer is Kate.

      1. tmtvl says:

        Hey, another KDE user, high five!

  8. Joshua says:

    Typo in the first sentence:

    “So the original problem is that programs turned into an incomprehensible mass. ”

    What language was the mass in?

    1. Jonn says:

      Also:

      … I might need to to look …

    2. Jake says:

      Is the typo you’re suggesting that mass should be mess? Because I didn’t see that as a typo. Programs can be perfectly comprehensible in individual segments, but over time it accumulates so much code – a mass of it, if you will – that you become unable to understand it not because it’s written confusingly, nor because it’s lacking documentation, but because there’s just so much of it that you have to search through dozens of functions to resolve a single function call.

    3. Syal says:

      Eldritch. The mass is a Shoggoth.

  9. Zak McKracken says:

    For some very strange reason, I read “dogmas” and for two seconds thought that you meant something like Christmas, but for/about dogs …

    1. The Rocketeer says:

      (10) And the angel said unto them, “Fear not: for, behold, I bring you good tidings of great joy, which shall be to all people. (11) For unto you is born this day in the city of David a Labrador, which is a very good boy, yes he is, oh yes he is.”

      1. Zak McKracken says:

        Thanks, you made my day, even if it’s just a midi/synthesizer using barks as samples

        That’s the kind of dogmas I can really get along with :)

  10. Zak McKracken says:

    So … how did you end up solving that problem for Good Robot?

    My first thought woud be to have each thing represented by an object which presents a few standard things, like its position and hitbox, and whatever methods are relevant to it (take_damage(), collide_w_wall_(), collide_w_player(), update(), draw_self(), …), and then a main loop which loops over everything several times: updates stuff (simply tell every object to update itself, which will give them new positions, states…) , then check collisions between any pair of objects as well as the walls. If anything collides, the main loop then calls the colliding objects’ .collide() functions, passing them the object they’re colliding with, and they can work out how to react (e.g.: bullets turn into a small explosion upon collision with the player, and the player will acquire the bullet’s damage, apply that to itself and maybe play some animation) — but for this the objects would only have read-only access to each other. Then the main loop resumes checks the states of anything that needs checking (update health bar, update screen to current player position, delete all objects which have met their demise), and of course draw every object at its latest position, in its latest state (sprite provided by the object itself).

    … is that to simplistic? I wouldn’t care too much about whether the main loop (or possibly rather a few subroutines of the main loop) is within an object itself or not, but I would try and make sure that the moving objects do not actually modify each other directly because then there’d be a real danger of race conditions where two different processes want to do the same thing to one object, and that might lead to bad things. Better to have that in the main loop. If necessary the main loop could compile a list of actions and remove conflicting ones before actually carrying them out.

  11. Zak McKracken says:

    From the way both Shamus and Brian Will describe it, it sounds as if there’s supposed to be only one True Structure to OOP code, in that any class can only be spoken to by another class which it inherits from — am I getting this right?

    I’ve never had proper lessons in object oriented programming (not quite true, I had about 3 weeks of object-oriented Pascal at some point in the 1990s…for whatever that’s worth) but my understanding has always been that you have two kinds of hierarchies in your code:

    One is about classes: If you have two classes, where one does almost the same as another one, only a bit different, then you can save a lot of extra coding and let the second inherit from the first, or make a base class which is then modified in different ways by the inheriting classes. That helps to save source code lines, and it also makes sure that if you decide later that some shared functionality should be modified, it will be modified for every class which uses it.

    The second is about execution: Who calls what and what can/should they do? If every function (or class/object/module… whatever) can call any other, that is dangerous because if you don’t take care you could have a() containing a call to function b() and function b() a call function a(), and you may just have built an infinite loop (unless you meant to use recursion …), so it makes general sense to have some sort of pecking order about who is allowed to call whom.

    In both Shamus’ post and Brian’s video, I get the impression that they treat both principles as the same thing, and as requirements for “pure” OOP, but I really only thought it was the first one that comes with OOP, and understood inheritance as an option not an obligation. The second one has been more or less natural to me way back when I was still using FORTRAN.

    Both, of course, can get in the way of getting things done if pursued with enough religious fervor. As an outsider (Programming is not my main job, just a tool), I have been exposed to many “real” programmers, but I always assumed like that was all general consensus…?

    1. From the way both Shamus and Brian Will describe it, it sounds as if there's supposed to be only one True Structure to OOP code, in that any class can only be spoken to by another class which it inherits from “” am I getting this right?

      This stuff is what makes me go Arggh!! and why so many have trouble with OOP. To answer your question, what OOP structured program means is that it is a WEB of object interacting with each other through a defined interface. You only need a hierarchy if that what make sense.

      For example I have a program that creates shapes to be cut on a metal cutting machine. There are multiple methods of cutting metal (plasma, waterjet, laser, router, etc). As well as multiple brands of metal cutting controller. So I wrote and maintain a list of controllers for each type of machine I have to interface with. Each every one talks to the software through a IController interface I wrote.

      It doesn’t matter which controller the user has pick as it known how to accept a list of shapes to cut, how to tell the software that it is done cutting, or that a error has occurred. And for stuff that unique to each controller for example the Status Lights. I abstract that into a general concept (like Status Panel, Control Panel, etc). So when a user want to use the control panel for a laser, the Laser controller object will throw up a control panel unique to it.

      Furthermore, libraries that the various controller reside reference a common controller library that the rest of the software doesn’t use. In this common controller library are routine that are commonly used by two or more controller types. The motion control industry has a bunch of standards for communications and other utilities, for example MOBBUS. So anytime a controller need to use MODBUS to communication I don’t have to copy and paste MODBUS code, I just reference the MODBUS routines in common controller.

      In the main software one section does have a hierarchy of objects. The software has a list of jobs. Each jobs has a list of sheets of different materials. Each sheet has a list of parts each with own rotation and location. Each part has a list of points. Job, Sheet, Parts, and Points form a logical hierarchy in how they interact.

      The least common thing that occurs is inheritance of behavior. For my software I defined a CAMObject. Every object in my software inherits from CAMObject. The CAMObject allows each of children to assign itself to another object of the same type, clone itself, save itself to a stream, and read itself from a stream.

      CAMObjectList also is defined and passes a common set of behavior to all the lists of objects I used. For example Insert, and Append methods are defined in one post.

      The point of using inheritance in this way to so that if I decide to change how objects are assigned to each other, there is one place to edit. The same for List and appending objects. It doesn’t happen often mind you, but when you maintain software for decades it will happen. And in the older version there are hundreds of object I had to touch to make a change in a fundamental operation. With good use of inheritance I avoid that and now there is just one place that I have to touch.

      Hope this helps

      1. Zak McKracken says:

        Dude, I use OO code all day, some of it in a similar fashion. In fact I’m supposed to work on some code right now. (but no, I’m not at work)

        That was not what I was referring to. What I was referring to is that the video seems to imply that “proper” OOP must use one strict hierarchy, otherwise it’s not “correct”. I think I phrased my comment ambiguously* but this is what I mean: Everyone seems to imply (but never explicitly states) that you Must keep some strict hierarchy in your code if you want to call it object-oriented, whereas I have been brought up to understand “code that uses classes and stuff” as object-oriented. Also, Shamus does talk about forcing inheritance between all robots, bullets etc. (the paragraph about “flyers”) in the same sentence as having a strict hierarchy for function calls. I understand him not wanting to bring up the separate hierarchies of inheritance and process structure but in either case, it implies the requirement for a strict hierarchy and neither Shamus nor Brian Wills state explicitly where that comes from and what exactly it refers to. They seem to assume that it is obvious but to me it’s not.

        So, when Brian says “OOP is a failure”, that sounds to me like “you should never use classes and stuff”, whereas he probably means “you shouldn’t force yourself to use *some implied system of rules for working with classes and stuff* exclusively, no matter what”. And I bet the difference between those two meanings is what fuels many a flamewar out there on programmers’ fora.

        Personally, I think saying “OOP is not the answer for every problem” would be way less contentious and would actually convey the meaning of the video better. It’s also way easier to digest because, well, there is no universal answer for every problem. Duh, who’d have thunk?

        * I’m aware of the irony of not phrasing my comment well while complaining about how things in the discussion are phrased — I suppose we’re all human.

        1. Geebs says:

          I always thought that “object oriented” means a bit of code that maintains some information about itself, can operate on that information in response to external messages, and is able to tell other bits of code about its current state.

          From that point of view, the bullet needs to know how to fly and hit things, and to tell whatever it hits how much damage it causes. The robot needs to know what to do with the information the bullet gives it.

          Basically that was a really long-winded way of saying that I don’t really get the controversy, and am probably happier that way.

          1. Zak McKracken says:

            Yep, I’m kind of having similar feelings. I’m actually trying to unravel what the big deal is.
            Thing is that I’ve been using what I (still) believe to be object-oriented programming, but some of the remarks make it seem as if that did not count as (pure? proper? actual? real?) OOP. …it feels a bit as if a large portion of programmers are not quite sure on the difference between any() and all()…

      2. Zak McKracken says:

        Here’s actually a good example of what I don’t get about this discussion. This was linked elsewhere in the comments:
        https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/

        It seems as if he is desperately trying to solve the game logic problem (that wizards cannot use swords and warriors cannot use staffs, but swords and staffs are both weapons) by using code structure, and by trying to define staffs within the wizard class and so on. So he’s building the hierarchy of who’s allowed to call whom directly into the inheritance structure. That can’t go well, and I don’t get why anyone even seems to assume that this was a valid approach in the first place.
        Now, I know thaty demonstrating the futility of this approach is the very point of the blog post but why does this need to be demonstrated? Where do people get the idea from that this was “what you do”? There must be some book, or common advice out there which I am blissfully unaware of which tells people to do it this way, otherwise there’d be no point in ever talking about it.

        I’ve only read the first post, but my approach would be to have one hierarchy of weapons classes and one of player classes, and each player class knows which weapons it can work with, and stores the instances (not the entire effing class!) of weapons it currently carries/has in hand. If you call player.equip(staff), the wizard will update its state accordingly, and the warrior will return an error. Where’s the big deal? Does this violate some theoretical principle, or why is that not the first approach that everybody comes up with?

        I learned pretty much all my coding skills by doing, so I keep thinking there’s some important theoretical background I’m missing, and the way both Shamus and Brian phrase their arguments seem to imply that this is so. Everyone is arguing against some dogma, but I don’t know what that dogma is, and noone seems to spell it out, at least not in a form I understand.

        1. Trevel says:

          If you read the last one you’ll discover that the entire thing is a trap and completely the wrong approach from the start, IIRC

  12. MadTinkerer says:

    So I have a couple questions:

    Asteroids, Space invaders, Galaxian, Galaga, Spacewar, Tank, Defender, Donkey Kong 3, 1942, 1943, and many, many other arcade and console games have player “objects”, enemy “objects” and bullet “objects” on the screen. But they weren’t made in OOP.

    So how did they do things (assembler obv., but that’s not what I mean), and why can’t that method translate to how we do things now?

    Also, what about this Entity Component Framework thing I hear so much about? I hear a lot of game engines use that.

    1. Phill says:

      OOP is essentially a way of organising complexity. When you’ve only got 32K of memory to play with in writing Space Invaders, the game code just isn’t going to be that complex. You can probably keep a decent model of the whole game code in your head at once.

      Once you’ve got a modern game with the code running to tens of megabytes, the complexity is far beyond weekday can be managed in old school style programming. Tools like OOP were developed precisely to impose a sensible (hopefully) structure to keep the massive complexity broken up into manageably sized chunks.

    2. I agree with Phill. In short the older methods don’t scale well. Also the older methods can do OOP given the expansion of capabilities in today computer. Behind the scenes object are can be viewed as data structures with function pointers as well as properties.

      However in assembly there is nothing but convention to enforce how things hang others. In modern OOP languages, the compiler does a lot of the heavy lifting of keeping things straight.

    3. Retsam says:

      Without any particular knowledge of those games’ code, they probably were just written procedurally.

      Suppose you’re implementing Galaga like that: you keep a list of enemies, with each enemy being represented by a small chunk of data (at the least a pair of integers for position, probably an integer to indicate what type of enemy it is), and maybe a similar list for projectiles.

      Then, each tick (each time you need to update stuff), you loop through all the enemies, and you do logic like “If this enemy is a red butterfly, then do [red butterfly logic], otherwise if this enemy is a blue hornet do [blue hornet logic], if this enemy is a Mr. Steals Yo Ship, then do [Mr. Steals Yo Ship] logic”. Then do a similar loop over the projectiles and update their position based on what type of projectile they are, and check if it collides with the player.

      That sort of lack of organization is fine for something as simple as Galaga with maybe a dozen enemy types and a handful of projectile types to implement, and very simple logic for each type. But, once you start juggling lots of enemy types, or more complex logic for each enemy (e.g. when each enemy has its own movement patterns), it becomes unwieldy and you start looking for different ways to organize the code.

      (Though my limited knowledge of assembly says that the above description is far too modern, and assembly programmers could only dream of something as powerful as a “list”.)

      TL;DR: It’s probably not that the old games had some ingenious coding technique that we need to adapt to modern games: it’s that with simple enough games, it doesn’t really matter what technique you use.

      IIRC, Shamus, when he was first learning C++, managed to implement Tetris without functions, with all the logic in a single massive function, (Simultaneously one of the most impressive and horrifying coding feats I’ve heard of) that’s not anything like a “good” way of doing it, but Tetris is simple enough that it was at least possible.

      1. Groboclown says:

        For the games I wrote for the Java 4k game contest, the code was just one giant method. There may be a few extra methods, but for the most part, you threw everything into one big code block to save on space. No objects, either – just big chunks of numbers referenced very, very carefully.

        This was similar to the old days when I was writing DOS programs (64k and all that). Memory was at a premium, so you had to use lots of tricks, which meant very little of the pointers-to-pointers that OOP tends to favor.

        These programs, though, were much smaller, much less complicated, and much more difficult to maintain over long periods of time.

      2. Syal says:

        Also remember that a lot of those arcade games had kill screens where the level would get high enough to roll over and break everything.

        Here’s an article on Galaga’s kill screen, though I’ll leave it for programmers to say whether it’s authentic.

    4. Zukhramm says:

      It’s entirely possible to write in an object oriented style, or something approaching it even if you’re not using a language made for i, it’s just a lot more work.

      Entity component systems are cool, and a good example of how something other than inheritance hierarchic is a much better way to structure the code of a game.

  13. Mike C says:

    Eric Lippert did a great blog series on this sort of problem in OOP, but applied it to a D&D-style game, where the interaction rules can get quite complex. (Example, a Wizard who can wield a staff but not a sword, strikes a werewolf with a +20-to-werewolves staff after midnight, which is when the werewolf gains 10% resistance to attacks.)

    It starts here: https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/

    Do you call Wizard.Attacks(Werewolf), or Werewolf.IsHitBy(Wizard)? Long story short, neither. The trick is to make the interaction rules into data, not code.

    1. Draklaw says:

      Hey, this is one of the source of inspiration that lead me to write two massive comments above. I no longer had the link, so thanks !

  14. Echo Tango says:

    “Meanwhile, someone like Brian Will crawls”
    I think that should have been “Meanwhile, someone from Barbara’s camp crawls” right?

    1. silver Harloe says:

      Since Brian Will is the author of the video Shamus is talking about, Shamus probably meant just what he said?

  15. Jack V says:

    I feel like, encapsulation is good, and you should do it as much as you can, but accept that where you can’t, you need interconnections.

    And object orientation is one prominent example of encapsulation, and using objects simple to apply scope to variables is basically “encapsulation formatted in a particular way”. And so many people equate object orientation and encapsulation, and say “object orientation is everything”.

    But in fact, I find that actual *hierarchies* of objects are not used that much. Some sorts of programming use them often. Some barely use them at all. And they’re a useful technique, but one among many.

    1. Daimbert says:

      Hierarchies solve a completely different and yet similar problem to encapsulation, as they primarily exist to solve issues of code duplication. If you have a bunch of similar objects that do similar things, you don’t want to have each class contain the code for the things that they all do, if for no other reason than if you update one you don’t want to have to remember to update all of the classes. But they also might do things — even those similar things — differently. So it’s nice to put all of that into a hierarchy that “lower” levels of the hierarchy can override if they have to.

      From the outside, with polymorphism, you get to simply call something like “handleDamage()” on the instance. Anything that does the same thing as everything else can simply use the default one (or the one one step higher on the hierarchy), anything that does most of that but only adds on to that behaviour can call the one one step higher and then do its work, and anything that’s radically different can override it completely.

      The link to encapsulation is that from the outside all the other objects need to do is call “handleDamage()” on the instance and don’t have to know or care if they do anything different or all work the same way.

      1. The problem is that real world problems don’t organize themselves in hierarchies with common behaviors in parents. Instead it turns out the common case is a web of things that need to interact with each in defined way.

        Where code is reused is in common libraries. For example anytime you need to use a serial port, you use the serial port library.

        1. Echo Tango says:

          I’d say that’s an overly strong statement, that real-world problems don’t work as heirarchies.

          As a counter-example, my workplace – there’s lots of code that is organized into hierarchies*. The thing I’ve learned from working there however, is that you often don’t know what the heirarchies will be, until you know more about the problem space, or new features have been added, or your code has grown in complexity. As such, it’s usually better to not spend too much time on making ultra-clean heriarchical code, and just make it good enough for right now. After some time, it’ll get refactored, and one of the refactorings that will probably happen, is that the code will be put into some (or better) inheritance hierarchies, to cut down on duplication.

          * We use Python, so it’s a mix of multiple-inheritance, interfaces, and strict inheritance. So, some things are strict heirarchies, and some areas of code are fairly complex.

        2. Richard says:

          And yet, a serial port behaves just like a file – esp. one on tape.

          In almost every case, the primary interaction your program has with serial ports is to use the File interface to write data, read data and check if there is data that can be read or space to write data.
          (Windows/Linux/OSX/VXworks/FreeRTOS… practically every OS and most bare-metal libraries as well)

          Eg:
          With C, you get a “Handle” for the serial port (as opposed to a file-on-disk) and use it with something that looks just like the standard File IO functions – read(), write(), getChar(), putChar() etc – except does the necessary to a serial port instead.

          With C++ you get an object that inherits a “File IO” interface and do the same set of things to it.

          In both cases the serial port library also adds a few extra functions that are specific to the serial port (baud rate), and disables some features that can’t exist (seek).

          In this example, the visible difference between procedural and OOP is simply whether the functions are inside or outside the “object” or “handle”.

          The key thing to remember about hierarchy is that it’s only behaviour that matters, nothing else. If something does not behave similarly to the very top-level thing in the hierarchy, then it belongs in a different hierarchy, or none at all.

          Don’t force it to fit. No single pattern fits everything.

        3. Daimbert says:

          Actually, in my experience, in real-world applications you quite often have very similar types of things that often share similar ways that they need to behave and also differ in certain ways. To take your serial port example, almost all serial ports will, I assume, have a “status” where they can be said to be Up or Down (connected properly or not). But different types of serial ports on different pieces of equipment or from different vendors might calculate that differently. All of them will have some kind of “line” status which is just “Am I plugged in and is the other side talking to me?” and if that isn’t Up, nothing is Up, but some of them might require certain other protocols to be configured properly before they can be said to be Up, and some are just dumb terminals that care about nothing more than “Cable’s plugged in”. In an OO heirarchy, you’d write a superclass — or maybe even abstract class — of “Serial Port”, add a method “getStatus()”, and in that one check if the cable’s plugged in. If you have a, say, “L3 Protocol Serial Port”, it extends “Serial Port”, overrides “getStatus()”, calls the “getStatus()” in the parent, and adds a check for the new protocol.

          From the outside, every client is given a “Serial Port” and calls “getStatus()” on it, and always gets whether the serial port should be considered Up or Down.

          I’ve seen this sort of thing a lot. Things aren’t always clean, but there are indeed a lot of cases where what you have are different types of Xs, where most of the time they all behave like Xs and only sometimes do they act differently.

          (Note that my descriptions of serial ports are based mostly on experience with telecom equipment, like large-scale routers, which may not be how you think of them.)

  16. Blake says:

    I feel the main issue is that a lot of people have a problem and their first thought is how to OOP-erise it, rather than looking for the best solution.

    Sometimes OOP and class hierarchies are good, but in a lot of cases they’re unnecessary cruft that is going to slow things down to write, read and execute.

    Most of the benefits people talk about with OOP (like encapsulation, shared code and interfaces (in the sense of how to interact with the data, not in the generically calling it sense)) are not specific to OOP, and C/C++ have lots of tools to accomplish those goals without putting everything into classes.

    If someone looks at a problem and decides OOP is the right tool for the job that’s fine, just as long as they’ve considered the added complexity that comes with it.

  17. guy says:

    I’m a bit confused as to what you consider “encapsulation”, since I don’t see how having robots and bullets as separate classes breaks it. I mean, I would make them subclasses of a joint parent, but that’s because they both move around the screen and do things when they run into obstacles and the boilerplate “move the sprite” and “check for collision” code can go in the parent, not because having Robot.getHitBy(bullet) is somehow a problem.

    The way I’ve been taught, encapsulation here just means that when the bullet hits the robot the bullet doesn’t go edit the hitpoint variable directly, it calls methods of Robot to handle taking damage. Whether the damage calculation happens in a Robot method that gets info from calling methods of Bullet or in a Bullet method that calls Robot methods isn’t inherently important. It doesn’t forbid objects from interacting with other types of objects, it just means they can only do so in specified ways, and allows internal changes without cascade effects. For instance, the discussion on a previous post about whether something should have HP and a dead flag if dead is synonymos with 0 or less HP; if there’s an isDead() method you can swap “return deadFlag” for “return hp <= 0" or back without changing anything else.

    1. Shamus says:

      Keep in mind that I’m pretty old, so I learned about OOP long after I became a programmer. As I’ve understood it, encapsulation requires / encourages objects to change ONLY their own state. The state of a robot is not part of the state of a bullet, therefore having a bullet call Robot.HaHaIAmDamagingYou() directly would be forbidden / discouraged.

      For the record, I think calling Robot.HaHaIAmDamagingYou() directly is the correct way to do things in the projects I’ve worked on, and it’s how I wrote GoodRobot. But it DOES have the problem where calling Bullet.Update () may cause a change to the state of robots.

      Judging by the comments here, it seems like there are a lot of competing definitions for “encapsulation”. And “OOP”. And “programming”.

      1. It the same problem with What the best algorithm when it comes to procedural. You have to agree on what to measure first and then after that examine the consequences of each algorithm (memory footprint, etc). The end result is that the only true answer is that you do your homework with a through understanding of the environment in which you will be using it.

        With OOP, you have a number of concepts at play that work together and work separately. Again the true answer is to do your homework with a though understanding of the environment it is running.

        What not common about my situation is that I been writing and maintaining the same software since 1985. And dragged it through several changes in computer hardware, programming languages, and operating systems. So I could compare how it worked with subroutines and function versus how it worked with classes.

        Also because we had to deal with the design of metal parts, cutting metal parts, AND reporting accounting details on what was cut. My hands are in nearly all the major areas of software development. I need to deal with 3D graphics, GUIs, databases, real time hardware control, etc, etc.

        In specific areas the software needs are not as demanding as other software. For example while I deal in 3D display of metal parts I don’t have to worry about hundreds of objects flying around a screen with various status variable needing to be updated in real time. Instead the most I have to deal with is a 100 to 200 facets of a complex part spinning under user control. Or dozens of variables while a part is being cut.

        What challenging is how all those has to work together not only to support each other in the latest version but to support legacy systems dating back to the 90s. (I think the last 80s era machine shut down 5 years ago. At least that the last time we got a service request on one of them.)

        After all said and done, OOP has been a huge winner. The extensive use of interfaces clearly defines how different parts of the software interact which is paying huge dividend as we transition to the .NET framework. I can write dummy objects on one side of the interface to do tests which save a ton of time compared to the other conversion I done.

        It also allows my small team of three programmers to maintain a dozen different metal cutting each with a distinct user interfaces. The use of classes and interfaces allowed us maintain a common engine for all these programs.

        And while the .NET conversion is mostly a mirror of what we are currently doing. The use of generics and inheritance allowed us to achieve one important goal that boilerplate code we use for lists of objects now resides in one location and any new general list features or bug fixes only have to be done once.

        Before we were using COM in the form of C++ and VB6 and COM only allows for classes and interfaces not inheritances.

        Just as important we still program in via subroutines and function in certain areas like the motion control hardware. There we are constrained on memory, available languages and processing speed so have to wring out every ounce of performance from the hardware.

      2. AR+ says:

        A problem of programing is that every term is grossly overloaded. I’m sure that “overloaded” itself is also overloaded somewhere.

        I remember a list of “features of object oriented programming” that listed a bunch of different things that have been described as “object oriented programming”, and their representation in different ostensibly “object oriented languages”. None of them ticked every box and they all disagreed on which ones they did. I’ve tried to find it but unfortunately could not.

        To wit, I’m a CS undergrad and by the instruction I’ve received, the important point is that objects not modify each other’s fields directly. So a bullet can’t say { robot.health -= 10; }, but calling robot.damage(10); is perfectly legitimate under OOP, because it still leaves all of robot’s internals to robot. Depending on how this is handled, this doesn’t necessarily require that bullet actually hold a pointer to robot in a persistent way.

        1. WJS says:

          I thought it depended on if things were public/private or not? Like, you might have Robot.takeFireDamage(), Robot.takeShockDamage() etc. which would call the (private) this.damage() method instead of expecting the damager to do it – is it “Robot.damage(20 * Robot.fireResist)” or is it “Robot.damage(20 * (1 – Robot.fireResist))”. “Fields bad, methods good” seems like an oversimplification to me.

    2. Draklaw says:

      Well, what you describe is not really encapsulation. The goal of encapsulation is to hide complexity. If the interface of your object is just a bunch of getters and setters that manipulate directly the fields of the object, you are basically exposing the internals of your object, so it is not encapsulated and do not really hides any complexity. Setters and getters give you some extra flexibility at the cost of a lot of boilerplate. But the benefits are limited because if the interface is too close to the implementation, it’s likely not generic.

      Ideally, you should design interfaces that hide how the object works. For instance, a “list” interface would contain methods to add, remove, reorder, iterate over items and so on, but should not expose if the implementation use a linked list, an array or something else, otherwise you failed to properly encapsulate the implementation. If done properly, you can change the implementation later for whatever reason, or have a piece of code that manipulate different objects through the same interface.

      1. guy says:

        Getters and setters provide a natural point for any input restrictions or conversions you might need, and the boilerplate isn’t an issue with IDE autogenerators. Of course, if something doesn’t need to be exposed you don’t write a public getter and setter for it (the safety checks provide a valid reason for using private getters and setters if you need to do something complicated every time you edit it) except to do unit testing.

      2. silver Harloe says:

        To Shamus’ point about different definitions. I’m approximately equally as old as Shamus, and also learned OOP late in my programming career, and the idea I got about encapsulation was this:

        A class is properly encapsulated when you can call it a black box that you need know nothing about the inside of to use it. You are given the interface, which tells you what will be returned from each method, and you can guess some of the internal state because the interface might say “call this method to repair and robot. call this method to damage the robot. call this method to see if the robot is still alive”. You can guess ‘hit points’ are part of the internal state of the robot, but you do not know. Even “getters and setters” are not a problem if they are an advertised part of the interface – you might have “get_hit_points()” and “set_hit_points()” as methods and think you are modifying a hit_points variable, but for all you know those are actually doing something complex internally and are not simply “getters and setters”. You can hand the interface to a programming team in Russia and they’ll code it up and if it passes the interface tests, it’s right. If you think it’s too slow, you can tell them and they might change *everything* about the internals, but you don’t know because it’s encapsulated behind the interface. “get_hit_points()” might transform from returning the hit_points attribute to adding up the elements of a list of statuses, and you don’t know. Nor should you care as long as the interface passes its tests and performs at the required speed and takes up no more than the required memory footprint (or, rather, your test suite should include speed and memory and similar stress tests)

        Of course, you DO know the internals of the class and DO care how its implemented because you never get the interface right the first time and always discover you need to change the interface and thus the internals. But that’s a practical consideration, whereas encapsulation is defined on the Platonic ideal code base :)

  18. Sabrdance (MatthewH) says:

    I have not previously been able to watch the video, but I’m on break now so I had 40 minutes. I’m at the part where he talks about the hierarchy.

    And what he’s saying is virtually identical to the arguments regarding hierarchical organizational structure compared to horizontal structure, and the long-running arguments about the need for centralization or limited span of control in bureaucratic organizations.

    So, do programmers walk over to the social sciences and ask them about organizational theory, because they are apparently talking about the same problem (oh, and in a bureaucratic organization, having sub-offices directly talk to each other really is a problem that can cause all kinds of practical, not to mention legal, problems, which may apply to programming, too.)

  19. JakeyKakey says:

    Shamus I get the feeling you’ll enjoy this .

    1. Tse says:

      Damn that was verbose… If the OS check is needed because each OS has a different behaviour in certain cases(so printing text would be just the first of many different functionalities), then I would make one class for each OS with a common interface and use a customizing table to decide which OS uses which class.
      I would never initialize multiple objects if I need to use just one of them.

      1. Kian says:

        The whole thing is wrong-headed. If you have a simple requirement, code the simple requirement. If the requirements grow in complexity, it’s easy to refactor the solution to add the new requirements. Even rewriting the whole thing if the changes are extensive enough.

        The approach they use, instead, is to write code so that nothing ever needs to be thrown away. Assume complexity will appear, and make your code preemptively complex to enable a future that may or may not happen. Which is fine if the unknown requirement line just right with your assumptions, but most likely the changes you need to make in the future will be very different to what you guessed with no information at the start.

        Maintainability is easier to maintain when the code is as simple as it needs to be, and no simpler.

        1. Tse says:

          Of course, if the requirement is to just output a string, my solution is to read the correct string from a table. I was just giving the authors the benefit of the doubt.

  20. Kian says:

    I agree with what Shamus writes here. My main objection to OOP is that it’s often applied to the wrong problem. One nice thing about C++ is that it lets you mix and match paradigms in a single application. To extend his metaphor, you get to use the road in the parts where the mountain is too steep, and climb up the side where the road gets too long. However, that means you need to understand both climbing and rationing.

  21. AR+ says:

    Have you read Gaming Programming Patterns?

    I ask because a lot of what you describe as the problems of game programming keep making me think things like, “but couldn’t you just…?” or “well, yeah, that’s why we…” or “this would be way easier to solve by just doing it the normal way…”

    …but I’m inclined to think that these responses are due entirely to having read things like the above patterns, which I assume are all hard-won victories against complexity developed over decades of time and centuries of programmer-years spent in the trenches of the industry. Hence why someone felt it worthwhile to write a book about them. With the benefit of other people’s (undoubtedly painful) experience, I can be suitably incredulous that things were ever actually done in the way you describe.

    (Did you know that the classic principle of “single return” did not originally mean what it does today, which is “a function should return from a single return statement”, but that “a function should return to a single point in the calling code. Ideally, the line after it was called from.” I don’t think most people who learned to program in so much a modern language as C would have even considered that as something you’d do on purpose, but you totally could, and people totally did. Now there’s some true spaghetti code!)

Thanks for joining the discussion. Be nice, don't post angry, and enjoy yourself. This is supposed to be fun. Your email address will not be published. Required fields are marked*

You can enclose spoilers in <strike> tags like so:
<strike>Darth Vader is Luke's father!</strike>

You can make things italics like this:
Can you imagine having Darth Vader as your <i>father</i>?

You can make things bold like this:
I'm <b>very</b> glad Darth Vader isn't my father.

You can make links like this:
I'm reading about <a href="http://en.wikipedia.org/wiki/Darth_Vader">Darth Vader</a> on Wikipedia!

You can quote someone like this:
Darth Vader said <blockquote>Luke, I am your father.</blockquote>

Leave a Reply

Your email address will not be published. Required fields are marked *