Wednesday, October 28, 2015

Programming Paradigms: Attaching Methods via Interfaces

Multiple Inheritance is Supposed to be Bad

Java and C# are very dominant languages and during their design they made some interesting choices. One notable design decision was to disallow multiple inheritance. I have heard the reason is because laziness and I have heard that the reason is because multiple inheritance leads to horrible design problems and the language designers are shielding us from this.

I tend to feel that shielding people from bad design is a questionable answer. Admittedly goto was removed from Java and that was probably a good idea, but the way you get to good design is to show people how to design things well and not to limit the tools. So I think what happened was that people could not use multiple inheritance so instead they used deep class hierarchies.

Deep Class Hierachies Are Bad

Sometimes I use deep class hierarchies, but I think most people would realize they are bad. They tend to create very hard to understand code. They also tend to clump together completely unrelated code in a highly coupled system. Object oriented programming tends to offer large class hierachies as the solution to design problems, but these hierarchies are hard to work with and understand. I was watching a presentation by a guy named Drew Petersen who was talking about the same ideas I am talking about here and he presented the following class hierachy for an Asteroids game:

WorldEntity
-> PhysicsEntity
   -> CollidableEntity
      -> RenderableEntity
         -> KeyboardCtrlEntity
            -> ShipEntity

Admittably, Mr. Petersen is presenting a particularly bad hierarchy, but this demonstrates many of the problems of this system. You have tightly coupled completed unrelated behaviors such as physics and rendering. To find the behavior of the system you often need to go through all classes in the hierarchy. Now this shitty class hierarchy is a direct result of not allowing multiple inheritance, but multiple inheritance may legitimately also allow incredibly bad design.

State versus Immutable State

I want to take an aside to talk about the concept of "mutable state" and "immutable state". Mutable state is something that can change during the runtime of your program. Immutable state is something that does not change during the runtime of your program. Let's take a quick look at the spectrum. Haskell does not have mutable state which is touted by it's proponents as a reason for it's awesome. JavaScript does not have immutable state which is touted by it's system optimizer as a significant problem. In JavaScript the inability to make assumptions about a function in the prototype chain causes optimization problems.

So now we have a notion of the mutable and the immutable we can also say that stuff being immutable has value for both understanding things and for optimization. Now in Java and C# you can attach keywords making some things immutable, but the main source of immutable state is the methods inside functions. This state is so frequently immutable that we often don't even think of it as state at all. When you call a method that method does not change underneath, but it is important to understand the mutability and whether you are a function are orthagonal concepts despite these important languages confusing them.

New Language Features: Extension and Default

Okay, to get back on track I want to point out two new language features that are different, but are very similar in what they allow you do. The first is extension methods. Extension methods allow you to write a static method with the first argument being an object with a type. Note that I said "type" and not "class". An object in C# or Java has a single class that may or may not inherit from one other class. It in turn can inherit from one other class creating a chain of inheritance that I was dissing earlier. A object can have multiple "types" because it can implement interfaces. This means you can attach methods (aka immutable state) to interfaces and an object can inherit immutable state from multiple sources.

Java has something similar where instead of attaching methods to arbitrary types you attach methods to interfaces directly. This feature is called default methods.

Attach Functionality with Interface - Abandon Class Hierarchy

Now we have a new paradigm for programming that dissects objects. An object goes from a container for both mutable state and immutable methods to a container for mutable state that borrows behavior from a series of interfaces. You remove methods from objects and they become very simple. Interfaces become simple and just deal with what they are supposed to deal with.

To apply this to the asteroids class hierarchy example above your asteroid, ship, and bullets are now just a collection or properties. You then attach stuff like physcis, rendering, and so on with interfaces. This leads to a much more composable system and one without so much tight coupling as created by deep class hierarchies.

Now this is a lot like multiple inheritance, but it is only multiple inheritance for methods (aka immutable state). This presumably avoids many of the pitfalls of multiple inheritance in a design.

Hey, You Ain't got no Privates

Now, object oriented lovers will probably notice that I have removed privates from the system. One of the purposes of objects is to have private state that is only accessible by it's own methods. It is one of the key advantages of objects. You can reduce exposure of details by making stuff private.

So that is true, but the goal of "private" is to help create an understandable system and if another paradigm leads to more understandable code then isn't it a superior paradigm worth abandoning privates to? Also, if you really want privates you can still create them with some trickery or you can use object composition to limit information access.

Summary

Programming is hard and we have to continually strive to find patterns that are easy to understand. I think object oriented, single inheritance languages like C# and Java have led people down the wrong path, but have snuck in other ways of programming as they struggle to stay relevant as better languages gain popularity. Attaching methods via interfaces and keeping object themselves extremely light and focussed on mutability has enormous value as an approach.


No comments:

Post a Comment