【问题标题】:Objective C - creating concrete class instances from base class depending upon typeObjective C - 根据类型从基类创建具体的类实例
【发布时间】:2012-07-04 06:57:48
【问题描述】:

举一个真实世界的例子,假设基类是 Vehicle,具体类是 TwoWheeler 和 FourWheeler。现在车辆的类型 - TwoWheeler 或 FourWheeler,由基类 Vehicle 决定。当我使用 alloc-init 方法创建 TwoWheeler/FourWheeler 的实例时,它会调用如下所示的超级实现来设置 Vehicle 类中定义的公共属性的值,并且在这些属性中,其中一个是实际决定类型的类型是 TwoWheeler 或 FourWheeler。

   if (self = [super initWithDictionary:dict]){
     [self setOtherAttributes:dict]; 
      return self; 
    }

现在,当我收集车辆时,其中一些可能是 TwoWheeler,而另一些可能是 FourWheeler。因此我不能像这样直接创建 TwoWheeler 或 FourWheeler 的实例

Vehicle *v = [[TwoWheeler alloc] initWithDictionary:dict];

有什么方法可以创建基类的实例,一旦我知道类型,根据类型创建子类的实例并返回它。使用当前的实现,它会导致无限循环,因为我从具体类调用超级实现。

当我不知道应该事先实例化哪个具体类时,处理这种情况的完美设计是什么?

【问题讨论】:

  • 那么你想要一些特殊的setter方法来有条件地改变你的对象的类吗?
  • 你为什么要让你的基类决定类型,基类甚至不应该知道类型!

标签: objective-c ios oop


【解决方案1】:

通常这是通过Factory 完成的。

如果您希望工厂成为基类的一部分,那很好,但将来可能会导致问题。在 Objective C 中,类方法是好的工厂。

+ (Vehicle *)vehicleWithDictionary:(NSDictionary *)dict
{
    if ([[dict objectForKey:kVehicleType] isEqualToString:@"TwoWheeler"]) {
        return [[[TwoWheeler alloc] initWithDictionary:dict] autorelease];
    } else if ([[dict objectForKey:kVehicleType] isEqualToString @"FourWheeler"]) {
        return [[[FourWheeler alloc] initWithDictionary:dict] autorelease];
    } else {
        return [[[Vehicle alloc] initWithDictionary:dict] autorelease];
    }
}

工厂可以是 Vehicle 类的一部分并按原样使用。

// Instead of [[Vehicle alloc] initWithDictionary:dict]
Vehicle *vehicle = [Vehicle vehicleWithDictionary:dict];

更新

我想出了一种方法来完成所要求的事情。让它成为一个光辉的例子,说明为什么这是一个坏主意以及为什么你永远不应该这样做。

- (id)initWithDictionary:(NSDictionary *)dict
{
    self = [super init];
    if (self) {
        // If override is in the dictionary, then it mustn't try to call the subclass.
        if (![dict objectForKey:kOverride]) {
            NSMutableDictionary *overrideDict = [NSMutableDictionary dictionaryWithDictionary:dict];
            [overrideDict setObject:@"override" forKey:kOverride];

            if ([[dict objectForKey:kVehicleType] isEqualToString:@"TwoWheeler"]) {
                [self release];
                return [[[TwoWheeler alloc] initWithDictionary:overrideDict] autorelease];
            } else if ([[dict objectForKey:kVehicleType] isEqualToString @"FourWheeler"]) {
                [self release];
                return [[[FourWheeler alloc] initWithDictionary:overrideDict] autorelease];
            }
        }

        // init as normal
    }

    return self;
}

【讨论】:

  • 感谢杰夫的回复。作为一种解决方法,我目前正在做同样的事情。但是想知道是否可以在事先不知道类型的情况下完成? Vehicle 类实际上决定了类型。
  • ... == @"TwoWheller... == @"FourWheller 并不明智。他们应该是[[dict objectForKey:kVehicleType] isEqualToString:@"TwoWheeler"]
  • @BenedictCohen ops……呃……我不知道你在说什么。一直是-isEqualToString: <_ ...>_>
  • @JefferyThomas 返回类型是否也总是丢失? ;-)
  • @indiantroy 这不是一种解决方法,而是一种解决方法。您询问车辆是否可以知道如何创建正确的子类。这就是它的完成方式。
【解决方案2】:

你应该使用抽象工厂如下所示,Vehicle 类将有一个名为 createInstance 的方法,该方法将有一个参数,该参数将决定创建什么,以示例为例

+ (Vehicle*) createInstance:(int)numberOfWheels
{
    if(numberOfWheels == 2)
    {
        return [[TwoWheeler alloc] init];
    }
    else
    {
        return [[FourWheeler alloc] init];
    }

    return nil;
}

你可以这样称呼它

Vehicle *v = [Vehicle createInstance:2];

【讨论】:

  • 我不确定您所说的“您的基类不应该知道子类的类型是什么”是什么意思,因为您正在显示基类(车辆)引用子类的代码( TwoWheeler 和 FourWheeler)。
  • 是的,你说得对,我没有写好上面的句子会更新它
猜你喜欢
  • 2017-04-10
  • 1970-01-01
  • 2021-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-30
相关资源
最近更新 更多