Tom Lilletveit
Tom Lilletveit

Reputation: 2002

Objective-C: Class design with properties

I want some ideas on how to program this class that needs all its variables and properties to be set beforehand for it to work. I was thinking to create an method to set the 3 CCSprites. But what about the properties? should I do an NSAssert to make sure they are set by the user, or is there a better way?

@interface DigParallaxBackgroundLayer : CCLayer
{
    CCSprite *background1;
    CCSprite *background2;
    CCSprite *background3;
}

@property (nonatomic, assign) float bg1ScrollSpeed;
@property (nonatomic, assign) float bg2ScrollSpeed;
@property (nonatomic, assign) float bg3ScrollSpeed;

@property (nonatomic, assign) CGPoint initialOffset;

Upvotes: 1

Views: 143

Answers (3)

Jasper Blues
Jasper Blues

Reputation: 28746

Its a great idea to provide the the class's collaborators externally, rather than have the class make its own collaborators. This is called Dependency Injection . . . (if you had the class set its own default values and collaborators, then wanted to change these you'd have to find all of the place's they're used and change them one-by-one. . . also the class would be hard to unit test).

. . . If you propagate this approach throughout your classes, you'll end up with a top-level assembly/wiring class that can be used to perform configuration management (sensible default values) as well as wire the components together into a functioning whole.

There are two varieties that could be used here:

  • Initializer injection: This is where the required collaborators are provided by an initializer method. One of the benefit of this approach is that its very easy to assert that the class is in a required state before continuing.

  • Property injection: This is where the required collaborators are set via a property setter. One of the benefits of this approach is that it promotes readability, when a class is composed of many other classes. A drawback to this approach is that it can be easy to get the object into an incorrect state. . . Some Dependency Injection libraries provide a call-back method to avoid this.

In the case where an object is immutable (ie the collaborators won't change at runtime) I like to use read-only properties and inject them via an initiailzer. This promotes a clear contract, and makes it easy to assert that the object is in the required state before continuing. . . . otherwise I like to use a method that gets called after property setters have been executed.

NB: assert is great for checking required state in debug-mode, but by default it won't be executed at run-time. (This may or may not be what you want).

Upvotes: 2

Aaron Brager
Aaron Brager

Reputation: 66234

Answer

You should set reasonable default values in your init method. You should also make sure the properties are set to reasonable values when you change them.

Note that NSAssert by default won't be executed in the production version of your app (you can change this behavior, but it's not recommended; see https://stackoverflow.com/a/6445429/1445366)

You can also create a custom class factory method. Something like:

+ (DigParallaxBackgroundLayer *) newBackgroundLayerWithDefaultScrollSpeed:(float)defaultSpeed {
    DigParallaxBackgroundLayer * newLayer = [[DigParallaxBackgroundLayer alloc] init];
    newLayer.bg1ScrollSpeed = newlayer.bg2ScrollSpeed = newLayer.bg3ScrollSpeed = defaultSpeed;
    return newLayer;
}

Further Commentary

When you start a new class with variable names like something1, something2, something3 you will almost always regret it.

It is almost always better to create an NSArray of CCSprites and another NSArray with your speeds in them. (You can use NSValue or NSNumber to wrap simple variables in an object so they'll go in an NSArray).

Even better, you could create another very simple class with properties background and scrollSpeed, and then put those in an NSArray.

Upvotes: 1

andyvn22
andyvn22

Reputation: 14824

Find reasonable default values for the properties, and/or create an initializer that includes values for each of them:

-initWithBG1ScrollSpeed:bg2ScrollSpeed:bg3ScrollSpeed:

Upvotes: 2

Related Questions