OOP Overkill

adriannull on June 27, 2018

Hi there, I've had for years a dillema that i just couldn't figure out. At some point in time i had to search for frameworks and CMSs, so i've t... [Read Full]
markdown guide
 

I really don't get why the concept of a class upsets so many people, even experienced programmers as in this case. A class is a very lightweight container. Complaining about classes feels to me like complaining about having to use files to contain code.

Classes will give you no benefit if all you do in them is to declare "public static" methods. In OOP, you embrace abstraction and polymorphism. If you don't, there is no benefit. It's like using functional programming without immutability or pure functions - it makes no sense.

The reason why people are arguing so much about OOP and so little about other paradigms (e.g. functional programming) is that the benefit of OOP is much more subtle and less apparent as other paradigms. A good OOP design starts to pay off when you are dealing with large applications, large teams and numerous cross-cutting business requirements. That's why Java and C# are the most popular languages for application servers.

Modularity, abstraction, encapsulation, composition and polymorphism are the core ideas of OOP. Can you build software without them? Sure. Can you do all these things without an OOP language? Sure. OOP is an idea, a concept - not a language or an implementation. If you look at the linux kernel source code, I'm sure you will find plenty of these concepts, even though it's implemented in C.

 

Thanks for your input. I perfectly agree with you. But i feel that some people have misunderstood me. I wasn't asking the benefits of using OOP at all, just the benefits of exagerating with it.

Like, let's take for instance, the idea of a car. One might create a class Car with all it needs, while many others would create class Car + class SteeringWheel: derived from Car + class Wheels: Car + class Engine: Car + class OnlyThePlayButtonFromTheRadioPlayer: Car and so on and so forth.

That's what i'm questioning.

 

Oh I see, I didn't get that part - sorry.

I would say it really depends on the use case. If you are dealing with, say, an E-Bay like second hand shop, then modeling the steering wheel of a car as a separate class is definitly overkill. But if you are writing software for a car manufacturer, then it could be totally fine.

I'm not saying that this overkill never happens. Sadly it happens all too often. However, there are two things to consider here:

1) OOP does neither protect you from, nor does it encourage, over-engineering. You can over-engineer a C program, a Haskell program, you name it.

2) An experienced software engineer will not overdo it with classes - and in particular inheritance - when there is no need for it. The most over-engineered code I've seen is coming from junior developers and students. More experienced programmers tend to be pragmatic about it, and that's a good thing. Thankfully, we have very powerful refactoring tools these days. So even if your initial solution turns out to scale badly (e.g. maybe you used an Enum that you now have to constantly expand) you don't have to stick with it forever, you refactor the problematic part into a more elaborate solution. When people come looking at this code afterwards, they may think that it is over-engineered. But they were not aware of all the cases which caused you to refactor the "simple" code in the first place. It's often easy to call a solution over-engineered by looking at it for 5 minutes. But to give a definitive answer requires a very deep dive into all the use cases.

There is also a very large "gray zone" here where it isn't always clear at all if an abstraction is necessary or not. Some people would say that, for example, the Spring Framework is a total overkill in this regard. As somebody who uses a very large portion of it in my day-to-day work, I would argue that it is perfectly on point.

And sometimes your program just needs to do enough things to warrant the number of classes.

Yes, I agree with this. It's important to strike a balance between the extensibility of the program and the mental overhead of numerous classes.

 

I would like you to Google a book titled Head First Design Patterns and read the first chapter on singleton pattern. You can probably find it on Amazon and read it in the preview. You're definitely missing the point of design patterns and principles if you don't understand why some developers write endless and meaningless single file classes that do less than one thing. What are your thoughts on test driven development if I may ask?

 

I agree...I have seem overkill use of OOP, specially when people over abstract or do dynamically things that should be static, but everytime I read people questioning OOP (and not doing functional programming) I cringe and hope to never come near their code. How do they TDD? They can't...
I would rather "navigate" through 400 classes 30 lines each then through 10 classes 400 lines each. And I'm being nice when I say 400 lines...I've seem true Megazord classes, with rocket lunchers and everything.

 

I am so using the term "Megazord classes with rocket launchers" from now on.

 

OOP and FP (Functional Programming) were invented to help developers adhere to SOLID principles while writing readable code. These are great tools and everyone should be well-versed in their use.

But Yes, anything can be overkill. For example, there is a rule of thumb to keep your methods small. The reason for this is to keep the code focused on a single purpose. However, in many cases it is better (more readable and maintainable) to have one long method (say filling out a large reporting structure with many fields) than to write a complex set of nested methods and classes to accomplish the same thing.

IMHO, Writing understandable, efficient and maintainable code is a blend of art and science. The more experience you have (esp. in picking up other people code and understanding it) the better you will be at it.

 

You mention using arrays (or stdClass objects I suppose), well the problem with passing arrays to a method or function is you then have to perform a bunch of checks to make sure you have all the elements you need.

If(isset($arr['foo']) && $arr['foo'] == 'A'){
  // DO THIS
}...

and repeat for however many elements you need to validate. And what if you have to pass that array to more than one function? Do you repeat all the validation, or do you abstract it, or do you rely on only being passed valid arrays from trusted callers?

What I do instead is write a dumb little class. It might only be 30 lines long but it'll be instantiated through a static method (or multiple static methods)

public static function createWithArray($input){
     $obj = new static();

     // do validation of array here.
     if(!$validated){
           $obj = false;
     } else {
          $obj->foo = $input['foo'];
          // I could also create and set other properties that I will KNOW exist based on observations of the input, which will make the code in my other function MUCH easier to read and understand.
          $obj->hasLongFoo = strlen($obj->foo) > 5;
     }
     return $obj;
}

Then back in my original function I change the definition to:

public function myFunc(\nameOfMyClass $myObj=null){
    // and I only need to check...
    if(!$myObj instanceof \nameOfMyClass)){
        return false;
    }

    // do stuff with valid object
    if($myObj->hasLongFoo){
         // do something with that information
    }
}

My function will only accept valid input.

This is how Swift works basically. You can trust your input and get on with writing clean code.

Massive additional benefit: My IDE (PHP Storm) understands what my object is and will tell me if I type any of the properties wrong. With an array you're on your own.

 
 

OOP can be overkill, especially if used as you described, with complex inheritance hierarchies. Python even supports multiple inheritance so you can really do wild things if you want so.

Want is the key though, you can NOT use complex hierarchies in your code or (as many do in many non functional language) try to use a functional style as possible.

My favorite features of OOP are composition and dependency injection I guess.

To answer your last question: I think that many people just don't think much about it and tend to follow the mass. If, as you highlighted, the framework you incorporate in your app uses objects and encapsulation heavily and the documentation does too, there's a high chance you're going to do the same. The framework might not even work without them :D

My personal experience is that forced encapsulation (aka really private methods or attributes you can't access from outside) can be an issue in the presence of bugs in the "private" logic.

The language I know best, Python, doesn't even really have the concept of private attributes. You can use _foo to tell the user that they are looking at a private attribute, you can use __foo to tell the user that they are looking at a really private (LOL) attribute but they can just access them or even change them if they really want to do so.

It's more of a convention than an actual barrier.

Python thinks programmers are grown ups ;-)

 

There are lots of reasons to use OOP for designing an application, though I am far from an expert.

When it comes to most personal projects or school projects, OOP style development is rarely necessary. There's usually not much repeating of code, the code isn't too complex and separated into many layers and modules. That's where OOP becomes useful.

However, in the workplace OOP becomes a bigger deal. When you've got dozens of project files, thousands of lines of code, and a complex workflow, OOP helps keep it all comprehensible. It allows for easier organization, less repetition of code, more readability, etc. Applications built on .NET MVC are a good example of this. Keeping things in separate layers of data (model), front-end (view), and function (controller) makes sure the code stays organized and that different modules don't overstep their boundaries.

For your last question, yes it can be a security issue. Part of application security is ensuring each module/segment of your code can only access the resources that it needs to access. Encapsulating your classes, keeping methods and variables private, etc. is a basic measure of security that can have catastrophic results if not implemented. This is actually the basis of a buffer overflow exploit, accessing resources that the module shouldn't be able to access.

 

You should listen to some Mike Acton stuff sometime. He dives into how oop shouldn't be the end all be all approach for everything. He's a big advocate of data oriented design. He can be a bit abrasive about it sometimes, but he's a pretty entertaining speaker. I think he's working for Unity now, which should be interesting to see if Unity starts embracing a bit of the data oriented design process down the road.

 

I'll look into this. Bit of searching, for something non-video, is this good introduction? gamesfromwithin.com/data-oriented-...

On OO, I read the following articles a while ago:

I am more in enterprise-development (sigh) than game-development, where Java is used a lot. The observations and critique on OO I found very recognizable.

When you strip away everything that sucks about OO, you end up with dynamic binding and a nice module system.

 

Thanks for the suggestion. I've found <CppCon 2014: Mike Acton "Data-Oriented Design and C++"> on Youtube, i'm listening now :)

 

If you follow the principle that a class should do only one thing then they often naturally become rather small and in a larger application there can be quite a few.

I personally find that approach appealing, yes there will oftentimes be small classes, but on the other hand once you get familiar with them its often easy to figure out what they're doing and how you should use them without looking inside them. And when you want to add some new feature/ability etc it's often easy also. I need another view, oh just add a class here, then use it in the code wherever you want.

I've also seen examples in production code where the powerful data structures of PHP have been used in functions thousands of lines long to build up to 8 dimensional arrays if not more. Try then to figure out what that code is doing without spending a few hours deciphering it.

Not saying that the code was good by any standard or programming paradigm, but it's what tends to happen if you do architectural decisions as an afterthought.

 

You brought a hot topic here my friend :)
I start with the basics :

  • OOP is useful, very
  • OOP si very often wrongly used

The mistake that can be seen very often is people thinking that methods calls are free or that every bit of their code needs a state.
Many classes should have their methods as static ones.
Many methods, especially from unexperienced programmers are useless or add performance or complexity issues just because they think OOP is the only way to go.
As examples :

public isMainSite():bool
{
   return IS_MAIN_SITE;
}

completely useless and a perf drop for no other reason than "i'm doing OOP"

or

private $myVar;
public function getMyVar()
{
    return $this->myVar;
}
public function setMyVar($value)
{
    $this->myVar = $value;
}

again: completely useless : if your setter and getter that do nothing then the property IS public by its use.
You can access class prop a lot more efficiently directly than through methods call.
Again something that few people think about: they learned "a way' to OOP and don't question it anymore.
So , OOP is useful, no douvt on this BUT is very OFTEN misused.

 

Actually, your second example isn't useless.

For sake of argument, your getter/setter is used in multiple locations throughout your application.

You've changed something/discovered a bug/etc. with that variable within your class when it's set to specific values. You can safely put in some input validation into your setter to only allow correct input values (you could even make it a boolean/return a success condition).

If you had direct access to the variable, not only would you have to create some sort of validation after the fact. You'd also have to change all references to the variable to now use your getters/setters.

This might be fine on smaller projects but in a large project or some sort of Framework/Library could cause lots of extra work.

In short, it's better to future proof in that case.

 

i got to disagree on the principes of future proof.
That is a conception key point :

  • or this variable needs validation and so it Requires Setter/Getter
  • or (as in the example I wrote) it doesn't need validation and so setter/getter are just one useless layer of abstraction.

The point you make that maybe in the future you'll need validation is very interesting: it confirms another OOP situations where developpers overlook properties roles and visibility.
But that is not an OOP requirement or practice it's a conception mistake that needs to be fixed.
My point is that many devs think setting private AND writing setter/getter is "the way to go" in all situations, that's wrong on many occasions.
Visibility/validation is an important part BUT of the conception phase.
I don't even mention security 'cause if it was not thought in conception something's wrong.

 
 

Code less do more, reusability, single responsibility, less tests. If your class has great namings, I think you can understand everything. Nothing is complex.

 

You have to ask your self why is OOP so popular? What is it good for? The key here is polymorphism. This is what allows us to be able to have inversion of control and in turn allows us to use SOLID.

With polymorphism I can build an application with extensions, allowing others to extend my application with out knowing or caring. I can also do things like composition to reuse code between various parts of my application. I can use inversion of control to insert a mock implementation of my datalayer allowing my tests to run fast. Or test any other part of my system in isolation. This is all possible in statically type languages using OOP.

 

I use interfaces and abstract types to define my contracts and I can easily replace the implementations with mocks or test doubles when writing tests.

 

I agree with you. Definitively I do not like the "everything is an object" approach or code with a hierarchy of classes with a very fine resolution. I remember a library that had something like "abstract socket," "abstract TCP socket," "socket that eats bytes", ... "RTP socket" and so on. Honestly, a mess.

I program in Ada (that allows OOP, but not forces it on you) and my choice if defining a class (a "tagged type" in Ada jargon) or a normal type is if I expect to need hereditary or polymorphism or if I need to "factorize" some common code. For example, in a program that reads different formats it can make sense to have an "abstract parser" that defines the interface that an "input parser" must have and then for every format there is a parser handling it. This makes much simpler to add new parsers.

 

15 years coding without use OOP hahahah, you code suck in every possible way.. and you just now are interested in known it benefits?
There is a simple explanation, PHP programmer, I have had the bad luck to have team up with your kind (with similar age like you), it took me years and even today when I have to review their code I know they don't get it
I don't even dare to ask your opinion about AOP (scary face)

 

Right, well good for you that you think yourself better than me and most others. For your information i'm not strictly a PHP programmer, i know several languages and i started with C/C++, so i'm familiar with a more strict language. Anyhow, i've got applications running 24/7, exposed to internet, and never had a problem because of my coding choices.
And btw, my post was not strictly about PHP, in fact i actually wrote it's a general trend i see.
You basically wasted your time writing a completely non-constructive reply.

 

I've been lately viewing it as having a gathering of different experts. I need to deliver some Mail. Then i need to request a Mailman to deliver(Mail mail). That expert may check that the Mail has all the information it needs to be sent. I don't do that checking, i just do my best to provide all the information that the Mailman can do it's job.

I don't need to know how the mail is delivered, it's not my expertise. It's actually nobody else's problem to know how to deliver mail.

A lot of folks dread Manager classes in code. Also in real life: i should know best HOW to do the thing i'm requested. I have those properties in me to do the job.

 

I can absolutely understand the author. In my career i saw a lot of oop code like:

report = ReportBuilder(a, b, c)
report.do_something()
result = report.get_results()

To me this is using OOP without using the brain. If it behaves like a function .. maybe it is a function. Besides this most of the implementations deserves a rant because this pattern guides the dev to initialize/load everything into memory!

My main question is the same as well. Why coders sometime choose the more complicated way?

  • Do they don't know/see the easier way (because of experience)?
  • Do they get enforces indirectly by (company/project) policies?
  • Do i am just wrong?
 

This really depends on what's inside ReportBuilder. Maybe it's a class that has to collect tons of data and do complex analysis.

In that case, it's super easy that you can use all that functionality in just 3 lines of code.

Also, don't forget that you are already using built-in classes every day! From using arrays to typing console.log(), all that functionality has to be programmed somewhere, and that's mostly in a class file.

 

Classes (or structure types, at a lesser degree) provide a language supported way for you to model your problem domain. What is a Customer? What is an Order? What are the possible states of an order? What actions can you perform on them? Go to those classes and read it up.

There is one place with the authority of defining each of those concepts. All places in the application that use those classes need to conform to it.

If you use associative arrays or dynamic objects, this central, authoritative, source of truth disappears, and now you need to carefully read all code dealing with some object to figure out what they do with it (which 'fields' they create, which kinds of values they assign, etc).

 

"I've never ever felt the need to take this approach"

That makes sense, because that need doesn't drop out of the sky while you are busy typing functional or procedural code. You're already comfortable and effective with a technique, so why change it?

You'll first have to spend some time stepping out of your comfort zone, learning how OOP works and how you can use OOP patterns to build something. It will take more time to achieve the same stuff at first, but sooner or later you'll have a few "a-haaaaa" moments.

OOP is not just about classes, but also about achieving a common way to structure code, making it easier to work in a team or work on someone else's code.

Have you looked at the MDN documentation for the DOM?. I think this perfectly illustrates a good use case for OOP inheritance.

MouseEvent is an Event that has some extra properties that only apply to events triggered by using the Mouse.

HTMLCanvasElement is an HTMLElement that has extra properties that only count for canvas elements.

 

Look up a design anti-pattern called the "God Object" as well. You want as many parts of your application as far away from each other as possible.

 
 

It's a valid paradigm for managing a large amount of state in an application. I consider that to be a flaw in itself and in that case the application should be split up.

code of conduct - report abuse