I am developing an application with a speedometer like animation (a meter and an arrow to indicate something on the meter). I store the arrow's position in a global variable decla开发者_StackOverflowred in my app delegate. I am doing it this way because the arrow position is updated and used by several classes.
However, I am insecure whether this is a good or a bad design decision. My thoughts are that since it is a non-critical piece of information (just a float), no harm is done when storing it globally. But my OOP heart hurts every time I say the word "global" to myself.
Alternatively I have studiet singletons, but as far as I have read, singletons are used when the developer wishes to create one and only one instance of a certain object.
Am I doing it correct or is there a more proper way of doing what I do?
I am doing it this way because the arrow position is updated and used by several classes.
in many cases, you can reduce the scope. this reduces inter-component dependency.
However, I am insecure whether this is a good or a bad design decision. My thoughts are that since it is a non-critical piece of information (just a float), no harm is done when storing it globally. But my OOP heart hurts every time I say the word "global" to myself.
perhaps you can move the state (the float value) to an ivar in your speedometer? example: you likely display just one speedometer view: does it make more sense to add it to what is the view's model? or perhaps to its controller? (yes, it's a bit tough to provide a more specific example without the source)
Alternatively I have studiet singletons, but as far as I have read, singletons are used when the developer wishes to create one and only one instance of a certain object.
not necessary, and a severe pain to maintain. most of the cocoa singletons i have seen should not have been considered singletons, and caused a lot of headaches. better yet, you can write programs which use zero singletons. this is ideal, and easy to test. as is, the programs/types which depend on the app controller's have been compromised wrt testability and reusability.
Am I doing it correct or is there a more proper way of doing what I do?
in the vast majority of cases, you can simply reduce the scope and localize it, while removing global state. with a little more effort, you can remove that value as a global -- that is best.
although it is not a good thing... let's assume you really really really really really must introduce global state:
- don't use a singleton. chances are good that you will rewrite it when you want to reuse it. it sugar coats what is ugly. if your app controller is a mess due to too much global state, at least the fact that you have too much global state will be obvious.
- hold your global state in your app controller. your app controller is responsible for its initialization, lifetime, and access.
- provide that state to dependencies, so they do not refer back to (or even know about) the global domain (the app controller). then you may minimize the impact.
there's also a distinct difference between global state and application/execution state. global state should be eliminated. execution state is not global state, but localized execution context. execution state can be reintroduced at the right level, altered, and updated, tested, and reused predictably. a good design will introduce execution state when needed, and at the right level while avoiding global state.
Update
Your sample is pretty close to what i had imagined, based on the description in the OP. It provided some additional specifics. So the sample below (you'll need some additions in obvious areas to piece it all together) demonstrates how you could update the controller interfaces, and there are two free 'elsewhere' methods at the end which further illustrate how to use these:
@interface MONArrowPosition : NSObject
{
float arrowPosition;
}
@end
@implementation MONArrowPosition
- (id)initWithPosition:(float)position
{
self = [super init];
if (nil != self) {
arrowPosition = position;
}
return self;
}
@end
@interface MyViewController1 : UIViewController
{
MONArrowPosition * arrowPosition; // << may actually be held by the model
}
@end
@implementation MyViewController1
- (void)applyRotation
{
[self rotateLayer:arrow from:self.arrowPosition to:callStatus speed:METER_SPEED];
}
@end
@interface MyViewController2 : UIViewController
{
MONArrowPosition * arrowPosition; // << may actually be held by the model
}
@end
@implementation MyViewController2
- (void)viewDidLoad
{
[super viewDidLoad];
/* ... */
[self.slider addTarget:self action:@selector(sliderValueDidChange) forControlEvents:controlEvents];
}
- (void)sliderValueDidChange
{
self.arrowPosition.arrowPosition = self.slider.value;
[self arrowPositionDidChange];
}
@end
/* elsewhere: */
- (void)initializeArrowPosition
{
/* The variable is set to a default of 0.0f */
MONArrowPosition * arrowPosition = [[MONArrowPosition alloc] initWithPosition:0.0f];
/* ... */
}
- (IBAction)someActionWhichPushesMyViewController1
{
// depending on the flow of your app, the body of initializeArrowPosition
// *could* be right here
MyViewController1 * viewController = [[MyViewController1 alloc] initWithNibName:nibName bundle:bundle];
viewController.arrowPosition = self.arrowPosition;
/* push it */
}
and then if MyViewController1 pushes MyViewController2, locating and setting the arrow position will be easy. the view controllers may also be sharing some information in the models. with a global in your sample, you are crossing many implementations, which adds coupling, increases dependency, etc.. so if you can take this approach and localize the execution state, you're off to a good start. then you can use any number of view controllers with any number of MONArrowPositions, and they will not be subject to the effects of global state. again, i can't get too specific using the samples provided, but i think this should illustrate the concepts i originally outlined well enough (i don't think a project-wide review is needed).
Well this is something that is keeping a lot of programmers up at night.
I try not to misuse the app delegate as much, I'll create a singleton for storing more or less global information. There is no real other way to do it then either the singleton or the app delegate.
But if only one viewController need the information than, the information will never leave that viewController. That viewcontroller could pass that information on to other viewcontroller is needed.
In you case it might be an idea to have some kind of directionManager which hold the floats and might even hold the CLLocationManager.
For this type of thing, I like to use NSNotifications. You have all the view controllers who care about the arrow's position listen for the specific notification, and they can all update the UI at once.
精彩评论