Why are the accessors for the PreferenceController written as class methods instead of instance methods? Aren’t the tableBGColor and EmptyDoc values essentially properties of our instance of the preferenceController? Is this because our preference controller is behaving like a singleton? When I think of Class methods I think of allocation/deallocation, convenience constructors, or “meta” behaviors like getting the count of all instances of a particular class. In this case, it seems like we’re operating on values of one particular instance–although there is no intent to create any others. Any thoughts on why class methods are the more appropriate choice in this situation?
I think the point is, that the class methods take care of the global variables not any instance variable:
extern NSString *const BNRTableBgColorKey;
extern NSString *const BNREmptyDocKey;
Maybe this is a good explanation too.
with best wishes
[quote=“Vertex”]I think the point is, that the class methods take care of the global variables not any instance variable:
extern NSString *const BNRTableBgColorKey;
extern NSString *const BNREmptyDocKey;[/quote]
Thanks for taking the time to respond. I take your meaning, but these methods aren’t really manipulating “global variables” (they’re global constants after all), but simply using those constants as keys to interface with NSUserDefaults. Perhaps it’s because PreferenceController is essentially wrapping the functionality of NSUserDefaults (which itself is a singleton), that the “accessors” to that data should also be thought of as more global in nature?
I have a pretty solid grasp of OOP principles and class design, but this example is making me question that understanding. I feel like I’m missing a design pattern, or principle that would explain this decision better. I can kind-of grok the why of it, but I never would have written my code this way. If this really is the best approach (and I’m pretty sure it is–Hillegass and the BNR guys really know their stuff), then I’m wondering what OOP principle/design principle I’m missing so my instinct would have been to use class methods here, instead of my gut reaction to use instance methods.
Thanks again for taking the time to post.
This is the basic idea, yes. The intention was to wrap the getters and setters for the defaults so that the details of using NSUserDefaults are abstracted away. A more OO-pure example would have created a dedicated class apart from PreferenceController (Preferences, say) and create the class methods on it, since those accessing the values of these prefereces don’t necessarily need to present a window for modifying them.
You mentioned that your gut reaction would be to use instance methods. In this case I would avoid instance methods, since they require the extra step of obtaining an instance. Of course, you could create a +sharedPreferences class method to get the instance, but it seems like an unnecessary step when these kinds of preferences are always going to be global.
Thanks for your very helpful response. Your points are well taken. So if one were going to write a maxim/principle/rule to cover this situation: “Always use instance methods to access the values of a class’ data except when…”
"…there will only be one instance of a class and the data that instance accesses is “functionally global”?
I hesitate to state anything involving design principles as though it is a maxim/principle/rule, because much of this comes down to personal coding style, but yes I think that’s a fair assessment.
Class methods on the Cocoa framework classes are quite rare, outside of convenience alloc-initializers and singleton accessors. The argument against using class methods is that sometimes you will find that what you thought would be global turns out to not be so, and then you’re stuck refactoring your class methods into instance methods, which isn’t much fun. In the case of simple user preferences like these, I’m willing to take the risk.
Indeed this is what triggered my initial question. I think perhaps s future edition of the book could benefit from a sentence or two explaining why class methods were the appropriate choice here, and also indicate that it’s usually not the appropriate choice in most other situations, or something to that effect.
Again, thanks for taking the time to reply. Appreciating the caveats of a given design-pattern (perhaps maxim/principle/rule were poorly chosen terms) is as important as grokking the pattern itself. You’ve been very helpful and I think my code will certainly benefit from the wisdom you’ve shared. Best wishes.