Prototype-based OOP – Advantages Over Class-based OOP

designlanguage-agnosticobject-oriented

When I first started programming Javascript after primarily dealing with OOP in context of class-based languages, I was left confused as to why prototype-based OOP would ever be preferred to class-based OOP.

  1. What are the structural advantages to using prototype-based OOP, if any? (e.g. Would we expect it to be faster or less memory intensive in certain applications?)
  2. What are the advantages from a coder's perspective? (e.g. Is it easier to code certain applications or extend other people's code using prototyping?)

Please don't look at this question as a question about Javascript in particular (which has had many faults over the years that are completely unrelated to prototyping). Instead, please look at it in context of the theoretical advantages of prototyping vs classes.

Thank you.

Best Answer

I had quite a lot of experience of the both approaches when writing an RPG game in Java. Originally I wrote the whole game using class-based OOP, but eventually realised that this was the wrong approach (it was becoming unmaintainable as the class hierarchy expanded). I therefore converted the whole code base to prototype-based code. The result was much better and easier to manage.

Source code here if you are interested (Tyrant - Java Roguelike)

Here are the main benefits:

  • It's trivial to create new "classes" - just copy the prototype and change a couple of properties and voila... new class. I used this to define new type of potion for example in 3-6 lines of Java each. Much better than a new class file and loads of boilerplate!
  • It's possible to build and maintain extremely large numbers of "classes" with comparatively little code - Tyrant for example had something like 3000 different prototypes with only about 42,000 lines of code total. That's pretty amazing for Java!
  • Multiple inheritance is easy - you just copy a subset of the properties from one prototype and paste them over the properties in another prototype. In an RPG for example, you might want a "steel golem" to have some of the properties of a "steel object" and some of the properties of a "golem" and some of the properties of an "unintelligent monster". Easy with prototypes, try doing that with an inheritance heirarchy......
  • You can do clever things with property modifiers - by putting clever logic in the generic "read property" method, you can implement various modifiers. For example, it was easy to define a magic ring that added +2 strength to whoever was wearing it. The logic for this was in the ring object, not in the "read strength" method, so you avoided having to put lots of conditional tests elsewhere in your code base (e.g. "is the character wearing a ring of strength increase?")
  • Instances can become templates for other instances - e.g. if you want to "clone" an object it is easy, just use the existing object as the prototype for the new object. No need to write lots of complex cloning logic for different classes.
  • It's quite easy to change behaviour at runtime - i.e. you can change an properties and "morph" an object pretty much arbitrarily at runtime. Allows for cool in-game effects, and if you couple this with a "scripting language" then pretty much anything is possible at runtime.
  • It's more suited to a "functional" style of programming - you tend to find yourself writing lots of functions that analyse objects an act appropriately, rather than embedded logic in methods attached to specific classes. I personally prefer this FP style.

Here are the main drawbacks:

  • You lose the assurances of static typing - since you are effectively creating a dynamic object system. This tends to mean that you need to write more tests to ensure behaviour is correct and that objects are of the right "kind"
  • There is some performance overhead - since reads of object properties are generally forced to go through one or more map lookups, you pay a slight cost in terms of performance. In my case it wasn't a problem, but it could be an issue in some cases (e.g. a 3D FPS with a lot of objects being queried in every frame)
  • Refactorings don't work the same way - in a prototype based system you are essentially "building" your inheritance heirarchy with code. IDEs / refactoring tools can't really help you since they can't grok your approach. I never found this a problem, but it could get out of hand if you are not careful. You probably want tests to check that your inheritance hierarchy is being constructed correctly!
  • It's a bit alien - people used to a conventional OOP style may easily get confused. "What do you mean there's only one class called "Thing"?!?" - "How do I extend this final Thing class!?!" - "You are violating OOP principles!!!" - "It's wrong to have all these static functions that act on any kind of object!?!?"

Finally some implementation notes:

  • I used a Java HashMap for properties and a "parent" pointer for the prototype. This worked fine but had the following downsides: a) property reads sometimes had to trace back through a long parent chain, hurting performance b) if you mutated a parent prototype, the change would affect all children that had not overriden the changing property. This can cause subtle bugs if you are not careful!
  • If I was doing this again, I would use an immutable persistent map for the properties (kind of like Clojure's persistent maps), or my own Java persistent hash map implementation. Then you would get the benefit of cheap copying / changes coupled with immutable behaviour and you wouldn't need to permanently link objects to their parents.
  • You can have fun if you embed functions / methods into object properties. The hack that I used in Java for this (anonymous subtypes of a "Script" class) wasn't very elegant - if doing this again I'd probably use a proper easily-embeddable language for scripts (Clojure or Groovy)