问题
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