【问题标题】:Cocoa style: using polymorphism in collectionsCocoa 风格:在集合中使用多态性
【发布时间】:2011-04-22 03:57:39
【问题描述】:

我有一个数据模型,其中包含大量异构项目(数组)。只有 2-3 种不同的项目,每种都继承自一个基类。使用经典示例,假设基类为Vehicle,子类为CarTrainPlane

我有一个更大的拥有模型/控制器,它想要在这个有序的车辆列表上进行操作,虽然一些操作是共享的(并且在基类中并在子类中被覆盖),但许多操作是特定于只有其中一种物品。

所以我最终得到了很多看起来像这样的代码:

for (Vehicle * vehicle in vehicles) {
    if (![vehicle isKindOfClass:[Car class]]) {
         continue;
    }
    Car * car = (Car *)vehicle;

    // Do stuff only with "car".
}

所以我到处都有很多-isKindOfClass:,并且很多将基类转换为子类。当然,这一切都有效,但似乎有足够多的“代码味道”让我认为可能有一种更优雅的方式来编写此代码或设计我的对象模型。

想法?谢谢。

【问题讨论】:

    标签: objective-c cocoa coding-style polymorphism


    【解决方案1】:

    我猜通常的多态模式是将循环体推到有问题的类中,所以你的循环变成了

    for (Vehicle * vehicle in vehicles) {
        [vehicle doSubclassSpecificThing];
    }
    

    接下来的问题是如何共享循环体的公共部分。你可以把它分成一系列的块:

    for (Vehicle * vehicle in vehicles) {
        /* common code */
        [vehicle doThingy];
        /* further common code */
        [vehicle doOtherThingy];
    }
    

    或者您可以要求 -doSubclassSpecificThing 先调用 [super doSubclassSpecificThing],然后将公共部分放在基类中(如果全部先到)。

    基本上,听起来你的循环体中发生了很多事情。如果您将每个部分提取到一个方法中,您可以选择共享或覆盖哪些部分,您的循环主体将成为对要做什么而不是细节的非常高级的描述。

    【讨论】:

    • 谢谢。这很有用,因为它告诉我我的设计一半错了,我的想法一半错了。我想说对象是半多态的更准确。有很多循环需要对对象进行操作。许多只是“doSubclassSpecificThing”,但有些是非常具体的(例如“setupAirbags”?在汽车上),不需要接触其他类型的对象;他们没有类似物。但是它们都在一个集合中,因为它们是有序的库存(延伸比喻,但是嘿)。您的讨论有助于澄清我的设计。回到思考板。
    【解决方案2】:

    如果您想访问特定于子类且尚未由其公共超类定义的行为,则无法逃避进行某种检查。多态性通常与定义在超类(或接口)中的行为相关,可以被子类覆盖,系统根据实际的对象类型知道哪种行为是合适的。

    也就是说,在您的示例中,特定循环对属于其中一个子类的数组元素的子集感兴趣,即Car。在这种情况下,您可以使用[-NSArray indexesOfObjectsPassingTest:] 创建一个仅包含Car 对象索引的索引集。完成此操作后,您可以迭代索引集,知道它指向原始数组中类为Car 的元素。例如:

    NSIndexSet *cars = [vehicles indexesOfObjectsPassingTest:^(id obj, NSUInteger idx,   BOOL *stop) {
        return (BOOL)([obj isKindOfClass:[Car class]]);
    }];
    
    [cars enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
        Car *car = [vehicles objectAtIndex:idx];
        // Do stuff that’s specific to cars
    }];
    

    【讨论】:

    • 公平点:“多态性”在这里有点误导。这些对象都共享一个共同的数据/功能存根,但都将该基础扩展到独特的行为。预过滤当然是一种更简洁的循环编写方式。
    【解决方案3】:

    我将消除强制转换并使用一组方法隔离 isKindOfClass 检查,这些方法将集合过滤为仅包含所需类型的元素。

    标题:

    @interface VehicleManager : NSObject {
        @private
        NSArray *vehicles;
    }
    
    @property (readonly) NSArray *vehicles;
    @property (readonly) NSArray *cars;
    @property (readonly) NSArray *planes;
    @property (readonly) NSArray *trains;
    @end
    

    实施文件:

    @implementation VehicleManager
    @synthesize vehicles;
    
    static NSArray *MyFilterArrayByClass(NSArray *array, Class class) {
        NSMutableArray *result = [NSMutableArray array];
        for (id object in array) {
            if ([object isKindOfClass:class]) {
                [result addObject:object];
            }
        }
        return result;
    }
    
    - (NSArray *)cars {
        return MyFilterArrayByClass([self vehicles], [Car self]);
    }
    
    - (NSArray *)planes {
        return MyFilterArrayByClass([self vehicles], [Plane self]);
    }
    
    - (NSArray *)trains {
        return MyFilterArrayByClass([self vehicles], [Train self]);
    }
    
    - (BOOL)areAllCarsParked {
        BOOL allParked = YES;
        for (Car *car in [self cars]) {
            allParked = allParked && [car isParked];
        }
        return allParked;
    }
    
    @end
    

    【讨论】:

    • 谢谢乔恩。这是最接近我的用例的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2016-03-04
    • 2012-10-17
    • 2021-08-08
    • 1970-01-01
    • 2010-10-06
    • 2023-04-11
    • 1970-01-01
    • 2013-11-18
    相关资源
    最近更新 更多