Best design pattern for un-archiving instances of shared resources?
Best design pattern for un-archiving instances of shared resources?
- Subject: Best design pattern for un-archiving instances of shared resources?
- From: "Barry Wark" <email@hidden>
- Date: Thu, 18 Oct 2007 17:06:36 -0700
Hi all,
Please forgive the cryptic subject. I'm in search of design advice and
obviously don't even know how to succinctly describe the problem I'm
facing. I know it's generally bad form to ask a question without code,
but since this is a design issue not an implementation issue, I
thought I'd start with description first (I'll be happy to send code
if needed). Let me explain what I'm trying to do:
I'm writing a data acquisition program for a physiology lab. One of
the classes in the application is a singleton instance of a Controller
for the A-D converter used to digitize signals from an analog
amplifier. The A-D converter has several channels, each of which are
represented (in my program) by an instance of a Channel object.
Because the A-D hardware can be reconfigured, the controller class
queries the hardware at program startup and indentifies the channels
that are available given the hardware configuration. The instance
diagram (after initialization) then looks like
Controller <-->*Channel
with the the available channels accessible as an NSSet* property of
the controller. Each channel is identified by a channel number (a
property of Channel) that corresponds to the hardware channel number
on the A-D converter.
Each channel object also has several user-configurable properties
(e.g. desired sampling rate, a user-visible description, etc.). The
Controller instance stores these properties by archiving (using
NSKeyedArchiver) the set of channels. The set of Channels can be
unarchived later to restore the user-set properties.
So, now the design problem(s) I've run into:
The Controller creates instances of Channels at startup but then wants
to replace these instances with the unarchived instances that contain
the users' settings. I can't skip the creation of the Channel
instances at startup because there may be channels that are now
available that weren't before (because of hardware configuration
changes in the A-D converter) and to simply replace the Controller's
Channels with the unarchived set would overwrite these new channels
and/or add Channel instances that correspond to channels that are no
longer physically present.
Finally, other objects in the program (such as the objects that
coordinate stimulus presentation and data recording) archive
references to the Channel instances when they save their settings.
When unarchived, obviously these objects want references to the
Channel instances being managed by the Controller, not the orphaned
instance that gets unarchived.
I see two possible solutions:
1. store the user-set properties in a separate object and archive that
rather than the Channel objects directly. This solution seems to
violate the "do it once" principle since this archived object would
have to keep information to identify the Channel instance it goes
with, duplicating a lot of information with the Channel object.
2. The solution I've chosen is to override Channel's initWithCoder: to
return the Channel instance that corresponds to the same channel
number, if it exists. This way, unarchived instances of Channel
automatically turn into the correct Channel instance (the one being
managed by the Controller) if possible. initWithCoder then decodes the
user-configurable properties of the Channel and sets the corresponding
properties in the Channel instance. In this way, user-configured
properties get set on the appropriate Channel instances when the
Controller unarchives its settings.
The problem I see with solution #2 is that multiple Channel instances
may be unarchived during the lifetime of the program (e.g. by the
stimulus coordinating objects described above) and I don't want these
newly unarchived instances to overwrite the user-configured
properties. Therefore, I've added a flag to Channel that indicates
whether the user-configurable properties have been initialized yet or
not. The pseudo-code for initWithCoder then looks something like this
- (id)initWithCoder:(NSKeyedUnarchiver*)decoder {
if(self = [super init]) { //super-class doesn't implement initWithCoder
int channelNumber = [decoder decodeIntForKey:@"channelNumber"];
id existingChannel;
if(existingChannel = [[Controller sharedInstance]
channelWithChannelNumber:channelNumber]) { //channel already exists
[self release];
self = existingChannel;
}
if(![self isInitialized]) {
...decode and set user-configured properties
[self setInitialized:YES];
}
}
return self;
}
with an init method that looks like
- (id)init {
if(self = [super init]) {
...set default values for user-configured properties
[self setInitialized:NO];
}
}
This works, but what to do in subclasses of Channel? We now need to
subclass Channel to provide specialized processing for different
channel types (and to add extra user-configurable properties
corresponding to these channel types). We could use a similar solution
in the subclass, with a subclass-specific isSubclassInitialized flag
which gets set to YES after unarchiving the additional user-confurable
properties. This solution has an odd smell to me. It seems to require
subclasses to know about and deal with a very implemenation-specific
detail of the superclass.
So, question for the gurus:
Is there a better pattern for solving the problems I've outlined
above, particularly one that doesn't require a similar hack for every
subclass?
If you've made it this far, Thank You! I look forward to any
suggestions you have,
Barry
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden