views:

68

answers:

3

Hello! I have a class that takes many arguments to create. It’s a sort of an audio processor that needs a sample rate, sample resolution, number of channels etc. Most of the parameters have sane defaults. And most of them should be only settable in the initializer (constructor), because it makes no sense to change them afterwards. I do not want to create a gargantuan initializer with all the parameters because (1) it would be huge and essentially it would only copy the passed values, doing no real work, and (2) the user would have to specify values for all the parameters. What’s a good way to solve this?

I’ve tried writing getters and setters for the params. This means that I could not create the “real” audio processing unit in the constructor, since the parameter values are not known then. I had to introduce a new method (say prepareForWork) so that the users can do something like:

AudioProcessor *box = [[AudioProcessor alloc] init];
[box setSampleRate:…];
[box setNumberOfChannels:…];
[box prepareForWork];
[box doSomeProcessing];

This is nice because it does not require an unwieldy constructor. Also the defaults are set in the initializer which means that I can take a fresh AudioProcessor instance and it could still do some work. The flip side is that (1) there is an extra method that has to be called before the instance can do any real work and (2) the class should refuse to change any of the parameters after prepareForWork was called. Guarding both these invariants would take some boilerplate code that I don’t like.

I thought I could create a special “preset” class that would look like this:

@interface SoundConfig : NSObject {
    NSUInteger numberOfChannels;
    NSUInteger sampleResolution;
    float sampleRate;
}

And then require an instance of this class in the AudioProcessor initializer:

@interface AudioProcessor : NSObject {…}
- (id) initWithConfig: (SoundConfig*) config;

Default values would be set in the SoundConfig initializer, the AudioProcessor constructor would be simple and there would be no invariants to keep watching by hand.

Another approach I thought of was a kind of AudioProcessorBuilder class. You would create an instance of that, set the audio params through accessors and then finally it would build an AudioProcessor instance for you, setting all the properties through a non-public setters so that you could not change them later (and break the invariant).

Now that I write this I favour the SoundConfig approach. How do you solve this, is there a better approach?

+2  A: 

See how NSURLConnection is implemented and used. It uses an NSURLRequest object to parameterise it, similarly to the way your SoundConfig object parameterises the AudioProcessor object.

Marcelo Cantos
+1  A: 

Usually, when you have class with many constructor parameters, especially when some or most of them are optional, you want to use Builder Pattern. Funny thing, I cannot find examples for objective-c, so I am not sure if it could be easily applied here...

Paweł Dyda
+2  A: 

The usual way is indeed to create a gargantuan initializer together with several initializers for non default values or sets of values.

-initWithSampleRate:numberOfChannels:sampleResolution:
-initWithSampleRate:sampleResolution:
-initWithSampleRate:numberOfChannels:
-initWithNumberOfChannels:sampleResolution:
-initWithNumberOfChannels:
-initWithSampleResolution:
-initWithSampleRate:
-init

But the SoundConfig approach looks simpler.

mouviciel
If going for the SoundConfig option, which I agree likes nicer, consider using NSDictionary instead. NSDictionary seems to be the Apple way of initialising objects with large numbers of arbitrary parameters.
JeremyP
Dictionary is nice because it does not add another extra class, but I can’t think of a really simple way to add defaults, and boxing sucks a big time.
zoul