Two worlds collided for me this week. There has been a ton of Internet chatter about Swift proposal SE-0117. Officially it’s titled “Allow distinguishing between public access and public overridability” but a lot of people are simply summing it up as changing classes and their members to be
final by default. This is the opposite of Swift 1, 2, and 3, as well as Objective-C and Java. Just as I was trying to keep up with the Internet discussion about this proposal, I came across Charles Scalfani’s article on Medium “Goodbye, Object Oriented Programming.” I could relate to every single point he made in that article, the first of which is that the biggest advertised benefit of object oriented programming, inheritance, is also its biggest shortcoming. After having professionally written object orient code for over 10 years, I absolutely agree. The thing is, object oriented programming is part of me.
Why Is Inheritance Bad?
In object oriented programming, inheritance is one of the fundamental principles that define the programming model. With inheritance, you can define one object to “inherit” from another object. This means that the child object has all the behavior of the parent object, as well as any new behavior it defines. It can also override behavior from the parent class when needed or desired. In his article, Charles Scalfani both gives a great description as to what inheritance is, with simple code examples as well. Take a look if you’re unfamiliar with inheritance. He also gives a perspective on why inheritance bad that I totally relate to. In my own mind, the most dominant piece of resentment against inheritance is the tight coupling that is created between parent and child class. The thing is, it’s so easy to think about classes in terms of inheritance. It’s so easy to understand that a square is also a rectangle, or that a dog is an animal. My mind naturally thinks like this. And back to the foundations of my programming education with C++, I was taught to think in terms of object oriented principles, and this means to look out for relationships between “things” that can relate to each other with “is a” relationships.
Often it’s easy to initially construct a design that appears to creatively leverage inheritance to remove redundant code and take advantage of that “is a” relationship. The thing is, as you maintain your code, make enhancements in response to new end user features, perform refactorings to clean up other pieces of code, you’ll quickly find the need to change either the parent and/or child classes in the inheritance relationship. This is where you will run into trouble. It will not be clear how changes to the parent class may affect the child class. On the flip side, it will not be clear how changes to the child class may violate assumptions made in the parent class. And in some cases, desired changes simply won’t be possible as it would break the “is a” relationship that inheritance brings.
Why Will Final By Default Be Better?
Not only is it easy to simply think in terms of inheritance when mapping out objects in your object oriented system, but both the languages and frameworks we use make it so easy to continue applying this anti-pattern. (Yes, I just went there. Okay, well not necessarily an anti-pattern, but a sledgehammer for all nails, even tiny finishing nails. Get it? Just because you have a tool doesn’t mean it’s appropriate for all jobs.) I’m not going to accuse all code authors as having not thought through the ramifications of inheritance, but think about it for a minute from the perspective of any class you might write. Is thinking about use of a
final keyword in the forefront of your mind to apply to the class or its members in order to prevent subclassing in times you don’t intend for it? I know in my experience, both in code I’ve written, as well as classes I’ve collaborated with others on, we almost never discuss whether it might be appropriate for others to subclass the given class, in order to correctly designate constructs as final. The conversation simply doesn’t happen, and thus the object is left open to be subclassed, either by us later on, or future team members, or in the case of open source code, or frameworks distributed to others, those consumers of the code.
The fact of the matter is, since this consideration hasn’t happened, there’s no guarantees that the class will work at all as a super class. What about as a super super class? Or a super super super class? Inappropriate subclassing could happen, totally be accident, and fast forward 1000 lines of code later, and you’re stuck with this incredibly tightly wound nest of coupled spaghetti code. Composition over inheritance is nothing new. By changing the fundamentals of Swift such that classes and their members will be final by default will go a long way to prevent accidental subclassing. This decision in the language will facilitate forethought at system design time to think and ponder the question, “Is there any reason for this class to be subclassed?” And only if the answer is yes, may you then use the proposed declaration modifier
open indicating that the class or class member may be overriden.
To The Future, and Beyond
Honestly, it’s a struggle to continue to wrestle my mind away from object oriented programming techniques. Like I already wrote, these are fundamentals driven into my brain since adolescence. Not only that, but it’s also very naturally to look for patterns between “things” in the manner that inheritance conveys, and then program them that way. The thing is, as easy as this is to do, I’ve also seen how ugly it can get. And just like Charles Scalfani proposes, it’s a false premise to think that inheritance leads to maintainable code. What side of the fence are you on with this debate?
As a bonus, if you’ve gotten this far, I’m reminded of a cheesy programmer joke:
What’s the object oriented way to get rich?