I've tried to describe the application I'm building in a much detail as needed, so I apologise in advance for the essay!
I'm in the process of designing and building a fairly large music application, using the C++ Juce framework, that in a nutshell takes in OSC messages and turns them into audio and MIDI data. The application has three 'modes', each defining what kind of sound will be produced by the OSC messages. The user can apply a mode and a further bunch of mode settings to define the sound that each OSC message 'triggers'.
Below is a basic block diagram overview of the programs' class relationship and hierarchy, or at least how I theoretically imagine it to be. To clarify the Juce terminology, a 'Component' class is basically a GUI object/class that displays things on screen and allows user interaction.
Basic block diagram http://liamlacey.web44.net/images/Software_block_diagram.jpg
I'm an experienced C programmer, however I'm fairly new to C++ and OOP design. I'm understanding most if it fine but the major problem I'm having is in terms of structuring all the classes to have the correct relationships and hierarchy so that they can all communicate properly in order for the application to do what it needs to do.
Here is a brief description of what each class does:
OscInput
- this base class uses the oscpack library to listen for OSC messages. Only 1 class can inherit from this base class as the application will crash if there are multiple listeners on the same UDP port.
Main
- application start-up. Inherits from OscInput so that every time an OSC message is received a callback function is called within this class
MainWindow
- the apps main document window - default to Juce apps.
MainComponent
- the main/background component/GUI of the app - default to Juce apps.
Mode1Component
/ Mode2Component
/ Mode3Component
- a single instance of each of these component classes is called and displayed from MainComponent which are used by the user to change the settings of what each OSC message does.
SubComponent1
- a single instance of this component class is called and displayed from MainComponent.
SubComponent2
- 48 instances of this component class are called and displayed from SubComponent1. Each instance is used to display the value of a different OSC message being received.
Mode1/Mode2/Mode3
- a single instance of each of these classes is called from Main. Each class is used to actually convert the OSC messages into audio or MIDI data, based on values/variables within the Settings class.
Settings
- a single instance of this class that is used to store settings that control what sound is produced from each different OSC message.
I'm fairly happy I have all the component/GUI classes laid out and connected in the right way. I have also got incoming OSC messages working fine. But it is the relationship of the Settings class instance that I'm not quite sure how to implement. Here are the relationships I need help with:
Therefore I have the following questions:
Where should the Settings
class instance be called from so that all the relevant class instances mentioned above can communicate with it? I only want a single instance of this class that needs to be accessed by many other classes, so should it be a global, Singleton, or static class? I've been studying the Singleton design pattern which seems to be what I'm looking for, but I get the impression I should avoid it if I can and consider alternative methods.
Should it be the Main
class that listens for OSC messages? How can I get SubComponent2 to receive OSC messages as well as the Mode1, Mode2, and Mode3 class instances?
Should the functionality classes (Mode1, Mode2, and Mode3) be called from Main? I'm trying to keep all functionality and GUI code separate as I have someone else dealing with GUI programming while I'm dealing with the functionality programming of the application.
Can anyone spot any major flaws in the design pattern of my program?
Any help would be greatly appreciated!
Thanks
Concerning your questions on "Main": you should not mix "application startup" with the responsibility for the message processing in the same class / component ("separation of concerns"). What you are describing smells like an application of the publisher/subscriber pattern
http://en.wikipedia.org/wiki/Publish/subscribe
And if you want to make your architecture really message-oriented, where not everything depends on "Main", and "Main" does not depend on everything, I suggest you have a look at "Flow Design". Look here
http://geekswithblogs.net/theArchitectsNapkin/archive/2011/03/19/flow-design-cheat-sheet-ndash-part-i-notation.aspx
http://geekswithblogs.net/theArchitectsNapkin/archive/2011/03/20/flow-design-cheat-sheet-ndash-part-ii-translation.aspx
Implementing a settings class instance as a singleton is ok, when you need those settings almost everywhere in your program. At least it is better testable than a static class. But you should avoid putting too much things in there, since a lot of things may depend on the settings, which may have a negative impact on the maintainablility later on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With