What should be done with inherited factory methods?

末鹿安然 提交于 2019-12-22 20:04:20

问题


Suppose I have a class BasicDate, and a subclass of BasicDate called EuroDate. The difference between the classes is month-day-year versus day-month-year. I know it'd probably be better to just have methods on the same class to output them differently... but that's not the point of this question.

BasicDate has the following init method:

-(id)initWithMonth:(int)m andDay:(int)d andYear:(int)y {
    if(self = [super init]) { /*initialize*/ } return self;
}

And the matching factory method then looks like this:

+(BasicDate)dateWithMonth:(int)m andDay:(int)d andYear:(int)y {
    return [[BasicDate alloc] initWithMonth: m andDay: d andYear: y];
}

But if my subclass, EuroDate which would use a factory method more like this:

+(EuroDate)dateWithDay:(int)d andMonth:(int)m andYear:(int)y {
    return [[EuroDate alloc] initWithDay: d andMonth: m andYear: y];
} //we can assume that EuroDate includes this init method...

This is all fine. Now, we assume that both classes have their own description method, which will print MMDDYYYY for BasicDate, but DDMMYYYY with EuroDate. This is still all fine.

But if I do this:

EuroDate today = [EuroDate dateWithMonth:10 andDay:18 andYear:2013];

This will call the BasicDate factory method that EuroDate has inherited. The problem is, remember how BasicDate's factory method looks? return [[BasicDate alloc] ...]

So today polymorphs into a BasicDate despite me wanting to store it as a EuroDate, so if I call the description method, it will print 10182013 rather than 18102013.

There are two solutions to this problem I have found.

Solution 1: Change BasicDate's factory method. Rather than return [[BasicDate alloc] ..., I can instead do return [[[self class] alloc] ...] This works and will allow me to use this method for BasicDate or any of BasicDate's subclasses and it will return the right object type.

Solution 2: Override the factory method. Whether I override it to throw an exception or override it to do return [[EuroDate alloc] ...]. The problem with overriding it is that I have to override every factory method for every subclass.

Which is better? What are some downsides to the two possible solutions that I may be missing? What is considered the standard way of handling this issue in Objective C?


回答1:


You should generally use [[[self class] alloc] init...] in factory methods to ensure that they create instances of the correct class. Note that class isn't a property (and in fact, there's no such thing as a 'class property') so the use of dot syntax there is inappropriate.

Edit

And as pointed out by @ArkadiuszHolko (and Rob, thanks), you should now use instancetype rather than id for the return value, to get the benefits of strong typing while maintaining type flexibility for subclasses. And by the way, Apple's naming conventions suggest avoiding using the word 'and' in method names. So consider rewriting your convenience method like so:

+ (instancetype)dateWithMonth:(int)month day:(int)day year:(int)year
{
    return [[self alloc] initWithMonth:month day:day year:year];
}


来源:https://stackoverflow.com/questions/19451744/what-should-be-done-with-inherited-factory-methods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!