如果您添加另一个Shape,您的方法的缺点就会出现 - 您最终不得不更改此代码。另一种方法是向您的Shape 类添加一个方法,例如userSelected,并让每个子类覆盖此方法以执行适合它们所代表的形状类型的任何事情。
在另一种方法中,您的循环变为:
for(Shape *shape in shapeManager)
[shape userSelected];
例如,Circle 获得一个方法:
@implementation Circle
// override Shape's userSelected and do what a circle needs to do
- (void) userSelected
{
...
}
您选择哪种方法取决于您,没有正确答案本身,但面向对象的风格通常倾向于后者。
评论跟进
要从子级转换为父级,您不需要强制转换,但可以包含强制转换 - 这是 Obj-C 等基于继承的面向对象语言的基本组成部分。
反过来,从父到子,在 Obj-C 中 (a) 需要强制转换并且 (b) 应该受到检查保护 - 两者都是因为您不知道您的子代中的哪一个(如果有的话)有。在 Obj-C 中,(a) 只是告诉编译器你认为你知道并且它应该信任你,(b) 你是否正在检查以确保!
您 (b) 使用 [<obj> isKindOfClass:[<classname> class] 来测试 <obj> 是否属于 <classname> 类型 或 它的任何子项;或者您可以使用isMemberOfClass 来测试<obj> 是否属于<classname> 类型但不是它的任何子级。前者更常见,以便于扩展(例如,您可以从 Rectangle 开始是 Shape,然后引入 Square 作为 Rectangle;对 isMemberOfClass:[Rectangle class] 的测试将是 YES Rectangle 和 Square 而isKindOfClass:[Rectangle class] 仅是YES for Rectangle)。
通常不需要显式测试,因为方法分派通常包含它 - 如上面的[shape userSelected],它将在运行时根据shape 的实际类型调用userSelected 的适当实现。当需要进行显式测试时,使用测试和强制转换就是这样做的方法。
是的,混合类型的集合很常见,事实上,它们是 Obj-C 等基于继承的面向对象语言的另一个组成部分。
[注意:Obj-C 缺少许多其他语言所具有的一种方法来限制集合可能包含的内容。例如。 NSMutableArray 可以包含 any 对象,而在您的情况下,能够说 NSMutableArray of Shape 将其限制为 Shape 及其子类可能会有所帮助。 C#、Ada 甚至 C++(尽管它强制执行任何东西)都提供了这一点。如果需要,您可以在 Obj-C 中自己完成,请参阅this answer。]