DEV Community

Mahi
Mahi

Posted on

Why I don't seal my classes—and why you shouldn't either

Most programming languages nowadays support sealed classes (sometimes final classes), which prevent the class from being extended or subclassed. Some languages seal their classes by default, like Kotlin, while others require you to explicitly seal them with a modifier keyword, like C#.

Now I don't just prefer the C# way, but as a matter of fact I have never sealed a single class of mine. And neither should you.

What is sealing, really?

Simply put, sealing is equivalent to claiming that:

I know better than those who come after me. I know what others will need this class for in the future even after I've left the company. I also know all the possible use-cases and what external users and clients will ever need this class for.

Now I don't know how good the lead developers in your company are, but I'm personally not willing to make claims that require me to see the future with absolute certainty—thus I always leave my classes unsealed. Remember that not sealing your classes isn't an invitation for everyone to start inheriting from everything; those people would break something anyways and there's nothing you can do to stop them. Meanwhile sealing your classes is you deciding for every single other developer out there, and claiming that not one will ever need to make an exception. Bold claim to make.

What are the upsides of sealing a class?

Performance. A compiler can make certain optimizations based on the information that a class won't be extended. If you're working on performance critical code, then yes, sealing a class can be a reasonable thing to do.

That's it. Any other upsides one can think of are mere misunderstandings. Refer to the FAQ section below, which I will be updating to debunk these misunderstandings as they arise.

What are the downsides of sealing a class?

If you made a mistake in your class, nobody else can fix it in theirs—instead they will have to wait for you to fix the mistake in your original, sealed class. While a noble approach in theory, the novelty quickly wears off in practice when the project already has a thousand issues on GitHub and the maintainer is on a vacation in Majorca.

Sealed vs unsealed class representation: blocking inheritance requires EV to duplicate all of Car's code

Similarly, if someone needs a small variation of your class that you failed to predict 10 years ago, they're out of luck. Imagine having to rewrite the entire Car class from scratch because the ICE people sealed their classes and now you can't extend your EV from them. Or having to copy&paste the whole flying and rotor logic into a drone because people in 1990s didn't think that helicopters could ever get small enough to not carry personnel. "Sorry, this class is sealed to only carry people."

FAQ

Q: Good design doesn't require inheritance, a drone can be composed of smaller parts of a helicopter and both an EV and an ICE can be composed from common parts!

Ah yes, favor composition over inheritance.

Of course the full quote from Design Patterns: Elements of Reusable Object-Oriented Software (which popularized the whole composition over inheritance principle) is more akin to:

Ideally all reuse can be achieved by assembling existing components, but in practice inheritance is often needed to make new ones. Therefore inheritance and object composition typically work hand-in-hand.

Now that's a direct quote from Wikipedia, but I'm the one who wrote that Wikipedia entry after reading the original book, so I'll let it pass this time.

What's important to note here is that already in 1994 when the book was originally published did they acknowledge that ideally everything is composed but in practice inheritance is needed. Why? Because nobody is perfect, nobody can see the future, and nobody can predict all the possible use-cases. Miss one use-case in your sealed class's design and your clients are going to hate you in 10 years. This is when inheritance comes into play, and this is why you don't seal your classes.

Q: How to ensure nobody breaks my classes?

Don't give them push/merge permissions to your project. If they break the code on their end, that's not your problem. If I incorrectly extend Angular's components or Qt's Widgets or write an invalid extension for Firefox, did I just break their code or mine?

Q: But the fragile base class problem!

A rare niche case where changing a base class's implementation (cross-calls between its methods) after it was extended would break the extending subclass. How about let's not completely prohibit one of the most powerful ways of extending code after a new unforeseen use-case arises on the off-chance that we ourselves change the way our class cross-calls its own methods, no?

If you need to make such fragile changes commonly, then you're doing something horribly wrong. And if you only need them once or twice over a 10 year lifespan, like a normal developer does, then you can introduce the change as major breaking change and go as far as changing the method names while at it. As a matter of fact, over my 20 years of experience I have only needed to introduce a fragile base class change exactly once. And that was because the initial design had been wrong.

Top comments (0)