Framework Design OOP vs Other

Started by
4 comments, last by Kylotan 6 years, 8 months ago

Wasn't sure whether to post this in Game Design section, but it seems that that one leans towards actual gameplay design, whereas this topic is more about programming decisions. In any case mods can move it. 

Just want to say that this is the first in a series of questions I planned out oriented towards using proper OOP. 

A while back I came across a post on here that made me rethink my ways in writing OO code. I cannot quote the individual but he said something along the lines of "it's not OO programming if you're just throwing related functions and data in a class and calling it a day". It kind of struck a chord with me and I got to thinking how most of the code I had was just that, things were Objects for no reason. For example in my initial iterations of frameworks, I had an Application class, a WindowsInterface class, a Utility "class" that just had all static functions (because it was convenient to just write Utility::TransformVector or something like that). Everything was a class just because. 

About a year ago I decided to step back from that and write code without any classes at all just to see the difference. I rewrote my entire framework in that style and really never looked back since. I no longer have to worry about class instances, about what seem like useless restrictions like having to pass hidden "this" pointers in window parameters or managing objects that didn't even seem like objects to being with. 

So on to my question. I'm now reading Code Complete 2 (after having read 1 years back) and with everything I've learned I'm tempted to give OOP another go. With a renewed mindset and a renewed appreciation for constraint. However that also got me thinking of whether or not all things conform well to OOP, maybe things like general low level frameworks or systems programming are inherently anti-oop? Maybe I'm just trying to push for something that's not really needed at this point? The reason I came to that conclusion is because I'm re-reading some design chapters right now in CC2 and he speaks of designing software at high level first thinking of it like a house. Designating subsystems, then designating modules in the subsystems, then designing classes and planning their interactions and only then moving on to functional execution of class methods.

As I sat down to rewrite my code, I realized that it's really difficult to even begin. I can't specify subsystem interaction for example and as per the book I have to restrict subsystem interaction because "it's chaos if every subsystem can access every other subsystem". Well that's the way I have right now, I have a SoftwareRenderer, UserInterface, Resources, Windows, Application and GameEngine subsystems. I see no reason to restrict SoftwareRendrer to any one, and as of right now since I'm coding in C, all a subsystem has to do is include "SoftwareRasterizer.h" and it's good to go with making calls. It's flexible and convenient. 

So besides subsystem interaction, I'm also having difficulty breaking things down into meaningfully classes. I blame it on the fact that a framework is by definition a low level system, so the obejcts can't be really straighforward common sense abstractions, they must be EventListeners and FrameDescriptors, which are abstractions non-the-less but at a much less intuitive level. 

Despite being confident that I don't really need OOP to get the job done, it's nagging me that it's so difficult for me to find an OO solution to a framework. Does that mean that I don't understand what a framework requires if I can't easily delineate the objects required to create it? Should I still push to get there? Or are some things just not as suited for OOP as others?

Thanks.

You didn't come into this world. You came out of it, like a wave from the ocean. You are not a stranger here. -Alan Watts

Advertisement

There are valuable lessons to be taken from OO, but in the end it is something of an orthodoxy. Modern C++ is designed to use a fusion of procedural, object oriented, and functional programming techniques. You can stick rigidly to one of those paradigms, or you can pick and choose the pieces that work the best for you.

My advice is to use whatever combination of programming paradigms makes you (and/or your team) the most productive. But it's certainly worthwhile to learn all of the above. Maybe even spend a little time with a language that is more rigid in its adoption of a particular paradigm...

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]


it's chaos if every subsystem can access every other subsystem 

I am very interested in this statement, and would be very glad to hear the reason.

Most part of my engine is divided like :-

  • SystemRenderInternal
  • SystemRenderForOgre
  • SystemPhysicForUser
  • SystemPhysicInternalFactory
  • SystemPhysicContraintManager

They can access each other freely via service pattern :-


getService<ServiceRenderInternal>()->Update();

So far, I have around 100 systems+.  I am so glad that they can access each other.   I don't have any problem about it. 

Am I missing something?   Is it about performance?

If the reason is about encapsulation, I think the chaos can be alleviated mostly by careful thought e.g. minimize such call / draw diagram & verify if it make senses.

A core rule of OOP is that classes should have a single responsibility. All your big systems will have many, many responsibilities so are a bad place to start -- you'd be going "top down" making bad classes, and then trying to fix them afterwards.

Start with the smallest things when making a class. The class should enforce the invariants on that data and restrict the interface to it. 

5 hours ago, VanillaSnake21 said:

Does that mean that I don't understand what a framework requires if I can't easily delineate the objects required to create it?

What kind of framework? What problem are you solving?

To add to the above: object oriented programming is a fairly simple philosophy - i.e. the idea that a computer program is best thought of as a bunch of objects that interact, rather than as an process that operates on data, or a collection of mathematical functions that return values. It's important to note that every program, even an OO one, IS a process that operates on data, and in some sense it IS a collection of mathematical functions. The key difference is the way you think about your program, and the features the language gives you to help you think and work in that way.

To a large degree, object orientation is about making software easier to understand, so that maintenance is safer. It does this by apparently making some things a little harder to do in the short term, with the idea that these restrictions are for the best. For example, when you say "all a subsystem has to do is include "SoftwareRasterizer.h" and it's good to go with making calls.", you're talking about the speed with which you can get something onto the screen with a traditional procedural programming approach. And that's fine, until you start hitting more complex problems, such as "How do I ensure that I don't call the software rasteriser from 2 threads at the same time?" or "What if I draw things in the wrong order and the visuals are wrong?" or "Two parts of the program set different values on the rasteriser for their own purposes and mess up each other's rendering", or "I now need 2 rasterisers at the same time for slightly different purposes, but there's only one set of functions". Object oriented programming gives you one effective way to reduce or solve those problems, at the cost of you no longer being able to "just include "SoftwareRasterizer.h"".

One thing that follows from this is that people often see statements about good programming practice in the context of object orientation, and then believe that the statement is intrinsic to OO, but it typically is not. Example: "it's chaos if every subsystem can access every other subsystem" - this is true in pretty much any language. If any subsystem can see any subsystem, this typically means any subsystem can be dependent on the state of some other subsystem. This means a change to a subsystem can affect the behaviour of all other subsystems. And this in turn means maintenance of the project is very error-prone.

Modularity, abstraction, encapsulation, high cohesion, low coupling - these principles are universal in good software. Object oriented languages provide 'opinionated' ways to achieve these goals.

 

Now, to your actual post:

7 hours ago, VanillaSnake21 said:

it's nagging me that it's so difficult for me to find an OO solution to a framework. Does that mean that I don't understand what a framework requires if I can't easily delineate the objects required to create it?

Yes. Frameworks already exist in object oriented form and in object oriented languages. There's nothing intrinsically low level about them that means they need a C-style approach. There also isn't any good reason why you should consider yourself adequately able to correctly identify the functions and data that you need for a framework, but somehow can't combine that into objects. I suspect you are resisting certain things because they make life harder in the short term (e.g. the Softwarerasterizer.h issue above) but would help in the long term.

This topic is closed to new replies.

Advertisement