【问题标题】:What should be done with inherited factory methods?继承的工厂方法应该怎么做?
【发布时间】:2013-10-18 14:08:26
【问题描述】:

假设我有一个类 BasicDate,以及一个名为 EuroDateBasicDate 的子类。类之间的区别是月-日-年与日-月-年。我知道在同一个类上使用不同的方法输出它们可能会更好......但这不是这个问题的重点。

BasicDate 有以下init method

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

然后匹配的factory method 看起来像这样:

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

但如果我的子类 EuroDate 将使用 factory method 更像这样:

+(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...

这一切都很好。现在,我们假设两个类都有自己的description method,它将打印MMDDYYYYBasicDate,但DDMMYYYYEuroDate。这还是没问题的。

但如果我这样做:

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

这将调用EuroDate 继承的BasicDate 工厂方法。问题是,还记得BasicDate 的工厂方法是什么样子的吗? return [[BasicDate alloc] ...]

所以today 变形为BasicDate,尽管我想将它存储为EuroDate,所以如果我调用description 方法,它将打印10182013 而不是18102013

我找到了两个解决这个问题的方法。

解决方案1:更改BasicDate的工厂方法。而不是return [[BasicDate alloc] ...,我可以改为使用return [[[self class] alloc] ...],这可行,并且允许我将此方法用于BasicDateBasicDate 的任何子类,它会返回正确的对象类型。

解决方案2:Override工厂方法。我是否覆盖它以引发异常或覆盖它以执行return [[EuroDate alloc] ...]。重写它的问题是我必须重写每个子类的每个工厂方法。

哪个更好?我可能缺少的两种可能的解决方案有哪些缺点?在 Objective C 中处理这个问题的标准方法是什么?

【问题讨论】:

  • 您应该查找“指定初始化程序”模式。该模式描述了如何构建子类和初始化方法以及便利构造函数(即工厂方法)。

标签: objective-c inheritance polymorphism


【解决方案1】:

您通常应该在工厂方法中使用[[[self class] alloc] init...],以确保它们创建正确类的实例。请注意,class 不是属性(事实上,不存在“类属性”之类的东西),因此使用点语法是不合适的。

编辑

正如@ArkadiuszHolko(和Rob,谢谢)所指出的,您现在应该使用instancetype 而不是id 作为返回值,以获得强类型的好处,同时保持子类的类型灵活性。顺便说一句,Apple 的命名约定建议避免​​在方法名称中使用“和”一词。因此,请考虑像这样重写您的便捷方法:

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

【讨论】:

  • 我在问题中修正了我的点符号。谢谢。 Objective-C 不是我的母语。
  • 而且,正如@ArkadiuszHolko 的回答中所述,您现在还应该在工厂方法中返回instancetype。在instancetype 之前,所有工厂方法都必须返回id 以避免警告。
  • 应该是return [[BasicDate alloc] ... ]还是应该是return [[self alloc] ...]
  • return [[self alloc] initBlah];return [[[self class] alloc] initBlah];?如果我使用第一个,当我继承并调用子类对象上的方法时,它会返回正确的类型吗?还是我需要在超类中使用第二个,以便子类返回正确的类型?
  • @nhgrif 在方法中的 self 指的是方法的 receiver。在 class 方法中,这是 Class - 而不是 Instance(对象)。因此,在 class 方法中,您可以编写 [[self alloc] initBlah]。因此,当self 实际是BasicDate 时,这实际上与[[BasicDate alloc] initBlah] 相同。
猜你喜欢
  • 1970-01-01
  • 2012-07-12
  • 1970-01-01
  • 1970-01-01
  • 2020-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多