General approach to design || design patterns || app structures


#1

Greetings all,

I personally find this forum / community to be of great help and a regular source of inspiration. I had a conversation with a friend recently about it, and several other topics around programming and development, and thought I might extend it to this forum in the event a few people have some sage advice or a few pieces of wisdom to share…

As far as programming goes, I’m self-taught, as I’m sure many of the members of the Cinder community are, and never had the opportunity to establish much of a knowledge-base before finding my way here. I wrote my first line of code around three years ago, and have experimented with and worked with a handful of languages and frameworks since. I’d describe my learning experience with Cinder as pretty much stumbling around in the dark, waving my arms like an idiot, and occasionally happening upon a moment or two of enlightenment. I don’t have a background in CS, or any formal software development training/experience, so I’m pretty much just figuring it out as I go along, as I’m sure many people in this field are.

I was hoping to spark a discussion on the best way to approach planning / designing an app, or I suppose simply how different people approach structuring a small/medium/large application. I’ve read books on C++, poured through Cinder’s samples countless times, read books on design patterns, scoured google and stack overflow etc, but I often find it difficult to translate some of these concepts and theoretical knowledge to practical applications, but then again maybe that’s just me.

For example, If I have an app that has multiple ‘states’, what’s the best approach to controlling these states? If the states change based on user-interaction, or timers, or external events, is it acceptable to place all update+drawing code in a set of massive switch statements, delineated by an enum representing each state? These are questions that I feel go beyond the scope of things like the Cinder samples, which are excellent examples of how to approach certain technical problems, but not necessarily ‘big picture’ approaches.

One of the ways I’ve learnt several programming concepts in the past is by looking at | hacking at | breaking examples of other peoples’ code. However, large scale projects that are used in production or deployed aren’t exactly made publicly available for people to do this ( which is of course totally understandable ).

Perhaps people are willing to share how they overcame these hurdles in their own personal journeys of Cinder wizardry…

Craig


Learning c++ properly for cinder
#2

This is a very cool post and some good questions that I’ll definitely be following along with. Thanks so much for posting @Craigson and hopefully we can hear from some of those that have experience with these larger scale applications.

My background is very similar to what you describe and my experience so far has generally lead my down the ‘massive switch statement’ route, though I typically try to keep the entry point to my app organized and clean by breaking things down into reusable modules/classes when I can. I definitely have written some apps that are all in one single file though as well.

As far as my approach to an app design; I usually just start hacking on the parts of an app I’m excited about, and as I start figuring things out I kind of reflow/rewrite that part into a cleaner, more organized and finished module/class and then move on to the next part. Sometimes I’ll even start a completely separate project where I can just focus on one small part of a bigger app, and bring it over when it’s ready. Super scrappy and hacky but it’s worked for me thus far :smirk:

Thanks again for starting the discussion, hope to hear how other’s approach things!


#3

Hi,

welcome to Cinder and its forums.

Your question about what’s the best approach to writing a large application has an easy answer: there is no best approach, at least not a single best approach. Each challenge requires its own solution and it takes practice and experience to arrive at that solution.

Sometimes, you stumble upon a solution that works for most of your projects, though. Most of the projects I did in Cinder have the same structure. It’s based on the concept of the separate update() and draw() methods that Cinder exposes. After settings things up, I continuously call each object’s update method to advance their state and/or animation, then call each object’s draw method to render what needs to be rendered.

At the core sits a system that could be dubbed “scene graph”, which is just a rather large set of objects in a hierarchical structure. Objects can have one parent, but multiple children. You then organize your application by creating container classes (e.g. a window, a menu bar, a “game”) and item classes (a button, a menu item, an “enemy”) and arrange them into the hierarchical structure that defines your 2D interface, or your game. All classes have both an update and draw method. You call them by starting at the topmost instance and then continue down the hierarchy. Some items will be invisible or inactive, in which case you can ignore them. Their children will be automatically ignored as well and won’t be updated or drawn. Some items will spawn new ones, or destroy others. The structure will change all the time.

A system like that is easy to maintain and expand, because each class is relatively small and contained. It can have its own state (e.g. opened or closed for menu items, or shooting and exploding for enemy items). Its behavior is defined in the class as well, and you can expose methods like open(), close(), shoot() and explode() to control their actions. Your application will not be one huge script, but a number of smaller, easier to maintain scripts.

A word about design patterns and state machines: while each of them offers an elegant solution to a particular problem, they are just that: one solution for one, well-defined problem. Knowing what they were designed for, why it works and why it’s a good solution, is very valuable. Implementing them, however, especially in a language like C++, can be very tricky (one example being the Singleton pattern, which is notorious in C++ land). Choosing which pattern to use for your application is hard and you’ll often find that it won’t solve all your problems.

My suggestion is to use your own creative mind, your experience as a programmer and some of the thoughts and ideas that you find around the internet to come up with a solution that works for your particular, truly unique challenge. Most likely this will not be an off-the-shelf design pattern. Counter-intuitively, the most important goal of your project is not a bug-free application or a happy customer, it’s a maintainable, flexible, stable application. Only then will you be able to change and improve things on request, and so will others who have to read and expand your code. Write your big application as a collection of smaller components, some using a state machine, others using an enum or a couple of booleans, depending on the individual class. Choose what works best for you.

-Paul


#4

Hey there,

I think that once your application begins to reach > 1k lines, it’s time to start thinking about refactoring into reusable components. The state machine design pattern is perfect for that. You can find an example implementation here; however, it assumes that you’ve been following the tutorial series: http://lazyfoo.net/articles/article06/index.php
I think you can still extract some ideas from the article.

In the end, it does come down to a large switch statement. However, this design pattern removes the ugliness of putting it into your update() loop, and introduces OO inspired compartmentalization.

Another design pattern I enjoy using is the Entity Component System pattern. It’s a good way to abstract how objects in your app update and interact. You can find a lot of information on the web about it, and I’ve implemented it in a 3d Conway’s Game of Life app that you can find here: https://github.com/johnmarinelli/game-of-life-3d

If you have any questions or want to talk about it in more detail don’t hesitate to private message/email me. Patterns are a great way to reason about your code, and I personally love code architecture.


#5

Thanks for your thoughts/feedback - I figured starting a conversation is a good place to start. The internet is obviously a wealth of information, but sifting through it can be a nightmare at times. I’ve been in a situation many times where I’ve spent hours looking for something without really knowing what I’m looking for, and is one of those cases where someone with more experience might’ve been able to answer in a few short sentences.


#6

Hey Paul,

As always thank you for the considered response. I’m going to paraphrase most of what you’ve said as simply “just keep doing what you’re doing” which for the most part is reassuring! I’m particularly fond of

the most important goal of your project is not a bug-free application or a happy customer, it’s a maintainable, flexible, stable application

Thanks again!


#7

For anyone that stumbles onto this post in the future, a friend just introduced me to this site: https://teachyourselfcs.com/ and it looks pretty great.


#8

Just thought I’d add another voice to this as I was in a similar position several years ago. Lots of this experience i’m sure you can learn on your own but working with an experienced C++ software team on larger projects can accelerate this kind of knowledge massively. I would definitely recommend it if you can find some project work with more experienced programmers.

A design pattern that we used a lot at ROLI was MVC (Model View Controller) which I have also found useful when building larger Cinder apps. The core principle is separating the data (Model), core functionality / interaction logic (Controller) and the renderer (View) into different classes that then interact with each other. This allows lots of flexibility if you want to switch out a data set (Model) (to say visualise a new set of data) at runtime without digging into a class full of rendering logic. Or perhaps have lots of different versions of a visualiser / UI (View) that you can easily switch in and out without having to duplicate all the data (Model) and functionality (Controller) code. General tips / example for this:

Model:
This is probably just a struct that holds all the data / parameters for how the object should behave. It will likely have minimal / no functions.

View:
This only holds functions to draw stuff. It might also have a const reference to the model so it knows how to draw its self.

Controller:
This contains a reference to the model (and potentially also the view if required) and tells them what to do. This is the main touchpoint to the class from the outside, it will mostly be functions that manipulate / animate / update the data in the model, handle interactions etc.

This design pattern certainly isn’t suitable for all situations (which is why other patterns obviously exist) but its definitely a good exercise to write an app in MVC style to think about how to split the different functionality of your app into smaller parts. If you see your main class has lots of members for parameters, lots of drawing related functions and lots of stuff to handle interaction etc all in one, its probably an opportunity to separate some of the responsibilities into smaller classes.

Another general best practice that i’ve heard repeated a lot over the years is that composition is generally better than inheritance, in that: separate classes of functionality that are nested and owned by other classes generally lead to more flexible / maintainable code than an inheritance structure where new functionality is added in each new subclass. The latter (inheritance) can start out fine but in larger projects inheritance trees can grow into very messy ‘towers’ of virtual functions where it is not clear which base class holds the functionality you actually want.

Hope this helps.


#9

Really interesting thread. Echoing what some other folks have said: There’s not really a single right way to architect apps and it can vary strongly based on size, type of interactions and type of output, but there are a few basic principles. You seem to be on the right track though.

At Bluecadet we loosely use a mix of MVC as well, but sticking to it too religiously can be tricky in less hierarchical apps, so we often blend it with ECS. The core idea behind it, though, is something that often holds true in the apps that we build: separation of concerns and clear ownership.

What helps me the most is having a class do one specific thing. One goal is often the ability to design a header file to require as little documentation as possible. Naming is usually a good sign for the health of your architecture: If you can’t find a good name for your class or method, then it might be because it’s not 100% clear what it’s supposed to do (or it’s designed to do too many things).

I’ll also echo what folks said about building re-usable components: Designing most of your code with the mindset that it’s something you’d like to re-use on future projects in a different context and with the least amount of dependencies can give you a good amount of focus and clarity about how to design your interfaces.

When it comes to animation-based interfaces, separating your model/data/state from the presentation (animations, UI) is often helpful to stay in control of your app flow. The same goes for ownership: Be as clear as possible about which class/module is responsible for answering key questions like “What content am I showing?” or “How many users are currently interacting?” about your app.

The rest really depends heavily on your own process and team. We typically work together with designers who will be able to mock up and prototype interfaces before we jump into full software production. We then take those designs and identify key views/interface elements as well as larger modules (e.g. sensor input, rendering pipeline, scene graphs, content & data). Then we iteratively prototype our architecture and scenes by stubbing out individual components in a rough skeleton, eventually fleshing them out one by one.

Thanks for starting the thread and thanks to everyone else for sharing their process.


#10

Thanks for adding your thoughts and insights Ben!


#11

If anyone else stumbles onto this post in the future, this is a decent breakdown of an OpenGL implementation of MVC - http://www.songho.ca/opengl/gl_mvc.html