Objective-C中的对象层次结构

4
我被介绍给了一个Objective-C代码库,其中大约有50,000行代码,我估计25%左右是重复的代码。不幸的是,在这段时间里,代码库中大部分都忽略了面向对象的原则,而是选择复制和粘贴逻辑。呀!
我来自Java背景,很多这种重复的问题可以通过良好的面向对象编程来解决。在许多情况下,提取共享逻辑到基类中似乎是正确的解决方案。
但是,在我开始创建一堆基类并在派生类之间共享通用逻辑之前,我认为我应该停下来看看是否有其他可用的选项。在观看Ken Kocienda的2011年WWDC会议上的“编写易于更改的代码”时,他建议我尽可能将对象层次结构保持浅层。他没有提供任何硬性统计数据来支持他的观点,因此我想知道是否有我所不知道的东西。
我并不是Objective-C专家,因此我想知道在决定对象层次结构时是否有任何最佳实践。基本上,我想听听关于何时停止创建基类并开始使用组合而不是继承来共享类之间的代码的意见。
此外,从运行时性能的角度来看,是否有什么可以使我远离创建对象层次结构的因素?

3
在任何面向对象语言中,尽可能保持扁平化的层次结构并优先使用组合而非继承是良好的实践,但与Java不同的是,Objective-C添加了分类的概念,这有助于扁平化层次结构。还有类簇模式可以帮助保持私有类的私有性,并仅公开某些特定的公共接口,这种方式与Java提供的方式相当不同。 - rid
2
@protocol类似于Java中的interface,而id则相当于Objectid <protocol>类似于Java中实现指定协议(interface)的对象。 - rid
3
@seanoshea,如果你正在考虑进行大规模重构工作,我建议你看一下AppCode,它在重构方面比Xcode要好得多。至于由于深层次结构而带来的性能影响,基本上不应该与Java有任何区别。不同之处在于Java使用静态绑定,因此它具有比Objective-C更多的优化选项,而Objective-C使用消息传递,因此始终进行后期绑定。 - rid
1
感谢Radu推荐AppCode。我一定会好好看看的。从性能角度来看,争论Objective-C中深层次的层级结构是否是一个不好的想法似乎已经进入了纠缠不清的领域(特别是如果我可以将其保持在2-3个层级)。不过,我会从教育的角度去研究延迟绑定。干杯! - seanoshea
1
@seanoshea,Objective-C是后期绑定其实不应该影响你使用这种语言的方式,这只是一种实现细节。我唯一遇到问题的时候是在代码的性能关键区域,其中切换到宏而不是每秒发送几千次消息可以极大地提高性能(objc_msgSend()虽然没有太大的开销,但在性能关键的情况下确实可以看出来)。 - rid
显示剩余7条评论
2个回答

2
我之前写了一些关于从其他背景转到iOS的想法,包括Java。由于ARC的出现,有些事情已经改变了。特别是,内存管理不再如此突出。尽管如此,为了使内存管理变得容易,您以前所做的所有事情(使用访问器,使用访问器,使用访问器)在ARC中仍然同样有效。
@Radu完全正确,您应该经常保持类层次结构相当简单和浅(如您所读)。在Cocoa中,组合通常比广泛的子类化更好(这在Java中也可能是真的,但在ObjC中是常见的做法)。ObjC也没有抽象方法或类的概念,这使得某些类型的子类化有点尴尬。与其将共享逻辑提取到基类中(特别是抽象基类),通常最好将它们提取到单独的策略对象中。
看看 UITableView 及其委托和数据源的使用。看看像 NSAttributedString 这样的东西,它具有NSString 而不是 IS-A。这是常见的,并经常保持清洁。与所有大型对象层次结构一样,始终要记住LSP。我看到很多 ObjC 设计在有人忘记正方形不是矩形时出了问题。同样,这适用于所有语言,但设计时值得记住。
无法更改(值)对象是一个真正的胜利,只要您可以使用它们。
另一个你会很快发现的问题是,很少有像“final”或“protected”这样的“安全修饰符”(虽然有一个@protected,但在实践中并不是很有用,很少被使用)。来自Java和C++背景的人倾向于担心编译器执行各种访问规则的强制性。ObjC没有大多数保护的编译器执行(您始终可以在运行时向任何对象发送任何消息)。您只需要使用一致的命名约定,不要去探究私有方法。程序员的纪律取代了编译器的执行。在实践中,在绝大多数情况下,这种方式都可以正常工作。
话虽如此,ObjC有很多警告,而且你必须消除所有警告。大多数ObjC警告实际上都是错误。
我有点偏离了对象层次结构的具体问题,但希望它有用。

0

Objective-C 中深层次的继承关系存在一个主要问题,那就是 Xcode 对于理解和管理这些关系没有任何帮助。另一个问题是,与 Java 相比,Objective-C 中的任何东西都会变得复杂两倍左右,因此你需要更加努力地保持简单。

但我发现 Objective-C 中的组合方式有些棘手(虽然我无法确切说明原因),所以并没有“完美”的答案。

我观察到,在 Objective-C 中,小型子程序比 Java 中要少得多,而且很可能会在大部分相似的视图控制器之间看到代码重复的情况。我认为这在很大程度上是由于开发工具和创建新类的相对棘手造成的。

附注:我曾经重新设计过一个包含大约 55K 行代码的应用程序,正如你所发现的,其中大约有 25% 的代码重复,但还有大约 25% 的完全无用代码。(幸运的是,该应用程序已经被基本废弃了。)


另一个问题是,Objective-C 中的几乎任何东西都比 Java 中的等效物复杂约两倍,因此您需要更加努力地保持简单。这是一个非常奇怪的评论。我与 Java 团队的几乎每一次交谈都包括很多“嗯,我们不能在 Java 中轻松地做到 X”(这与我在其中开发的经验非常相似)。我从来没有遇到过创建新类的麻烦。我只有在存在复杂继承时才会遇到麻烦(但 Cocoa 设计不鼓励这样做;语言匹配了倾向于组合的设计选择)。 - Rob Napier
我绝不是说ObjC在继承方面不能进行一些改进(为受保护方法提供语言结构通常很有用,并且在类别周围存在几个小问题肯定可以改进。我的Android团队经验表明,那里完全可以采用自定义或100%的原始库(完全按照API开发人员的想法执行),但如果您想要稍微自定义一下,由于Cocoa大量使用委托,它似乎更简单灵活。这只是我的经验;我没有写过很多Android代码。) - Rob Napier
@RobNapier -- 我说的是编程语言,而不是UI平台。Android的设计很糟糕,与选择Java作为主要语言无关。 - Hot Licks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接