I'm trying to design and develop a software (part of a bigger application) that should compute and render a spectrogram.
The spectrogram can be rendered either from live microphone or a file (assuming its content has been turned into a float array) and using two different algorithms, i.e. FFT or DCT.
In case the spectrogram has been created using an FFT I want to be able to change its scale to mel and in case it was computed from file, also optionally apply filters like Band-Pass, Low-Pass, High-Pass, maybe something more in the future, with a given roll-off per octave (input parameter to the filter).
I started to sketch an UML (don't kill me if I didn't use the aggregation/composition notations, still have to learn), that looks like this (please ignore the notes, it's in my native language and are just addressing thoughts that the UML can't represent)
Basic idea: I have a
Spectrogram class that I use as an interface to the client. When they want to compute a spectrogram they instantiate an object of this class, configure it and call
computeSpectrogram(timeBuffer: Float*): UIImage? on it.
Under the hood the
Spectrogram delegates the computation of the spectrogram to its
spectrogramCalculator strategy. Each strategy reimplements
computeSpectrogram(...) and since different strategies from the same family (FFT or DCT) share some code, I will create a single point of change by collocating it in the
FFTStrategy as a protected method with a default implementation.
At this level, I need to be able to dynamically add/remove behaviors to the FFT computation (filters, scale change) and therefore I go for Decorator Pattern, that according to Design Patterns, Elements of Reusable Object-Oriented Software, can be used to
Attach additional responsibilities to an object dynamically [...]
But only for the FFT strategy. Also the
MelScaleDecorator can be written in a way that suits both
MelScaleMicrophoneDecorator so that they share the same implementation, so I may create a static utility
MelScaleUtils class that exposes a static method that both the decorators use to give their implementation of
Now my question is the following: Is this a suitable use case for the Decorator Pattern? Cause if the user wants to undo some operation (eg Band Pass filtering) I'd have to recreate the whole decorator chain and use the new chain to recompute the spectrogram (takes less than one second for a 2min long .wav file, which is fine for my requirement). That or either create an
ActionsQueue stack that stores
Commands (see Command pattern) to support an undo mechanism to compensate the lack of flexibility in dynamically removing the additional behaviors.
Am I overengineering it?