I was discussing some of the finer points of coding with my boss yesterday, and we’re somewhat diametrically opposed on some points. In the end, he brushed aside my suggestions as “style differences” but I think it’s more of a maintainability issue, and I was curious what the rest of you thought. I’ll try to present both sides neutrally.
When designing a system in an OO environment, is it preferable to have fewer, larger classes with similarly few methods, or many classes and methods, and at what point have you gone too far in one direction?
It feels like there’s a bit of a hole in the “few classes/few functions” vs “many classes/many functions” dichotomy. In a system with fewer classes, those classes will require many functions as each class will be dealing with more state and performing more tasks. So it’s “few classes/many functions” vs “many classes/few functions”.
I prefer to keep classes as small as reasonably possible. Imagine a single instance of your class in isolation, as its own application. Each instance variable is functionally a global! The bigger that this unit becomes, the harder it gets to test, maintain, manage, and reason about. Global state needs to be cached in your memory all of the time while you’re coding, so if a class has more than about 7±2 instance variables that need to be reasoned about, you’re going to make mistakes. Having many variables also vastly increases the number of potential “error” or “invalid” states your class can be in. Can you reasonably check for all of them?
Martin Odersky, describes “shared mutable state” as the root of all evil, and his language, Scala, focuses really tightly on forcing developers to either treat state as immutable, or keep it tightly under wraps. The mutability checking is statically compiled into the language. Seriously if it weren’t for rampant abuse of operator overloading and the ongoing nightmare that is the JVM, I’d be a dyed-in-the-wool Scala advocate. The bigger a class gets, the more shared mutable state that class has.
“As small as reasonably possible” can be hard to manage, though. Code wants to intertwine. How do you keep classes small and minimize shared state?
I seriously distrust deep inheritance trees. Not for C++ programmer reasons like “vtable lookups” - if I cared about performance I wouldn’t be a Python programmer. My problem with deep inheritance trees is the widening avalanche of state that gets bigger and uglier as you travel closer and closer to the object that you’re actually using. Reasoning about code at the bottom of the tree requires knowledge of every step up the entire chain. A small class in a deep inheritance tree is just a LARGE CLASS.
People who are stuck with Java are forced to favor composition/interfaces over inheritance ( https://en.wikipedia.org/wiki/Composition_over_inheritance ) - whereas C++ and Python offer multiple inheritance, and Scala and Ruby offer mixins and traits - making it possible to build very broad inheritance trees where individual classes only focus on managing small parts of the class.
Scala also borrows from Haskell a focus on allowing programmers to build functions that just take small immutable objects as arguments and produce small immutable objects as output, which is composable as fuck and easy to test and awesome to the power of cool.
okay I’m dithering all over the place here
Lots of small classes! Immutable if possible! Share as little as possible! No deep inheritance trees!