【问题标题】:Making Objective-C Classes look Beautiful让 Objective-C 类看起来很漂亮
【发布时间】:2010-12-09 06:27:50
【问题描述】:

我想就Objective C 中的代码异味,特别是Cocoa Touch 向大家征求意见。我正在开发一款相当复杂的游戏,并且即将开始 Great December Refactoring。

我的很多类,尤其是模型,都充满了处理内部业务逻辑的方法;我将把它们隐藏在一个私有类别中,在我与大量头文件的战争中。那些私有类别包含大量声明,这让我感到不安……几乎就像 Objective-C 让我对所有这些方法感到内疚一样。

我重构得越多(一件好事!),我就越需要维护所有这些重复(不太好)。就是感觉不对。

在像 Ruby 这样的语言中,社区非常强调非常简短、清晰、漂亮的方法。我的问题是,对于 Objective C(特别是 Cocoa Touch),你的方法有多长,你的控制器有多大,以及每个类有多少方法在你的项目中变得很典型?在 Objective C 中是否有任何由短方法组成的类的特别好、漂亮的示例,或者这根本不是语言文化的重要组成部分?

披露:我目前正在阅读“The Little Schemer”,这应该可以解释我的悲伤,回复:目标 C。

【问题讨论】:

  • 谢谢!当我开始思考回答 obj-c 问题比回答 ruby​​ 问题需要多长时间时,很明显这是一个严重的问题。呜呜呜……
  • 您可能对此stack-exchange proposal 感兴趣。它几乎可以开始测试了,只需要更多。
  • 维克多,非常感谢,看起来很棒。我现在正在学习 Clojure,而且周围根本没有其他人可以检查工作。我刚刚承诺!

标签: objective-c cocoa-touch cocoa-design-patterns


【解决方案1】:

是主观的。对我来说,如果一个 Objective-C 类是可读的(我知道它应该做什么)和可维护的(我可以看到哪些部分负责做什么),那么它就是美丽的。我也不喜欢被一个不熟悉的习语抛弃阅读代码。有点像当你在读一本书时,你读到的东西会让你脱离沉浸感并提醒你正在阅读。

你可能会得到很多不同的、相互排斥的建议,但这是我的想法。

  • 私有方法属于私有类别并没有错。这就是它的用途。如果您不喜欢阻塞文件的声明,请在 IDE 中使用代码折叠,或者将您的扩展作为一个类别放在不同的文件中。
  • 将相关方法组合在一起,并用#pragma mark 语句标记它们
  • 无论您使用何种代码布局,一致性都很重要。花几分钟时间写下你自己的指导方针(这里是mine),所以如果你忘记了你应该做什么,你有一个参考。
  • 控制器不必是委托和数据源,您始终可以为这些设置其他类。
  • 为方法和属性使用描述性名称。是的,您可以记录它们,但是当 Xcode 应用代码完成时,您看不到文档,其中命名良好的方法和属性得到了回报。此外,如果在代码本身发生变化时未更新代码 cmets,它们也会变得陈旧。
  • 不要尝试编写聪明的代码。您可能认为将一系列方法调用链接在一行上会更好,但编译器在优化方面比您想象的要好。如果可以提高可读性,则可以使用临时变量来保存值(大多数情况下这些只是指针,所以相对较小)。 编写供人类阅读的代码
  • DRY 与其他语言一样适用于 Objective-C。不要担心将代码重构为更多方法。有很多方法,只要有用就没有错。

【讨论】:

  • 我已经放弃了#pragma mark,转而使用// MARK:,它在Xcode 中的菜单上具有相同的效果,但不会在编译器之间可移植的代码中引起警告。此外,// MARK: - 是一个真正的帮助(也适用于编译指示)。它在菜单中放置了一条水平线。
  • 感谢您,这些都很棒,在大重构期间非常有用。
【解决方案2】:

在实现类或方法之前,我要做的第一件事就是问:“我想如何从外部使用它?”

我从来没有,从来没有首先编写我的类和方法的内部结构。从优雅的公共 API 开始,内部往往会免费变得优雅,如果它们不这样做,那么丑陋至少包含在单个方法或类中,并且不允许用它的气味污染其余代码.

有很多设计模式,二十年的编码告诉我,唯一经得起时间考验的模式是:KISS。保持简单愚蠢。

一些通用的经验法则,适用于任何语言或环境:

  • 按照您的直觉,对您阅读或听到的任何建议进行操作!
  • 提早纾困!
    • 如果需要,尽早验证输入并快速退出!减少清理工作。
  • 切勿在代码中添加您不使用的内容。
    • “reverse” 的选项可能会让人感觉不错。
    • 在这种情况下添加它!不要浪费时间添加您不需要的复杂性。
  • 方法名称应该描述做了什么,从不它是如何完成的。
    • 只要结果相同,就应该允许方法在不更改名称的情况下更改其实现。
    • 如果您无法从名称中理解方法的作用,请更改名称!
    • 如果方法部分足够复杂,请使用 cmets 来描述您的实现。
  • 不要害怕单身人士!
    • 如果您的应用只有一个数据模型,那么它就是单例!
    • 到处传递单个变量只是假装它不是单例,并增加复杂性作为奖励。
  • 从一开始就计划失败。
    • 从一开始就始终使用doFoo:error 而不是doFoo:
    • 从一开始就使用最终用户可读的本地化描述创建漂亮的 NSError 实例。
    • 将错误处理/消息改进为现有的大型应用程序是一件很痛苦的事情。
    • 如果涉及到用户和 IO,总会出现错误!
  • Cocoa/Objective-C 是 面向对象*,而不是 **类,就像大多数声称是 OOP 的流行孩子一样。
    • 不要引入只有属性的哑值类,没有执行实际工作的方法的类也可以是结构。
    • 让您的对象变得智能!如果您只需要 Foo 上的 fooFromString: 方法,为什么还要添加一个全新的 FooParser 类?
  • 在 Cocoa 中,你能做什么总是比 你是什么更重要。
    • 如果目标/操作可以做到,请不要引入协议。
    • 不验证实例是否符合协议,是一种类,这取决于编译器。

【讨论】:

  • Apple 更喜欢只有比例的类而不是结构(对不起,没有参考)。此外,在处理id、子类或可选协议方法时,自省可能很有用。
  • @Scott - Jupp,这在 ARC 介绍后发生了变化。 ARC 不能很好地处理结构中的对象引用,但可以处理类。
  • 完全同意这一点,也总结一下我的经验:)
【解决方案3】:

我的 2 美分:

  1. 属性 通常比老式的 getter+setter 更好。即使您使用 @dynamic 属性 - 使用 @property 声明它们,这也会提供更多信息且更短。
  2. 我个人不会为类模拟“私有”方法。是的,我可以在 .m(m) 文件中的某处写一个类别,但是由于 Obj-C 没有纯粹的方法来声明私有方法——我为什么要发明一个?无论如何,即使您真的需要类似的东西 - 声明一个带有类别的单独“MyClassPrivate.h”并将其包含在 .m(m) 文件中以避免重复声明。
  3. 绑定。绑定大多数控制器 UI 关系,使用转换器、格式化程序,只是不要编写手动读取/写入控件值的方法。它使代码看起来像 MFC 时代的东西。
  4. C++,很多代码在用 C++ 编写时看起来更好更短。由于编译器理解 C++ 类,因此它是重构的好点,尤其是在处理低级代码时。
  5. 我通常拆分大控制器。 超过 500 行代码对我来说是一个很好的重构候选者。例如,我有一个文档窗口控制器,因为它使用图像导入/导出选项扩展了某些版本的应用程序。控制器增长到 1.000 行,其中 1/2 是“图像内容”。这是我制作 ImageStuffController、在 NIB 中实例化它并将所有与图像相关的代码放入其中的“触发器”。

以上所有内容都使我更容易维护我的代码。对于一个大型项目,拆分控制器和类以保持它们的小结果大量文件,我通常尝试将一些代码提取到框架中。例如,如果应用程序的很大一部分与外部 Web 服务通信,通常有一种直接的方法可以从主应用程序中提取 MyWebServices.framework。

【讨论】:

  • #4 是一个非常糟糕的想法。我第一次听到有人说要翻译成 C++ 以使代码看起来更好。
  • jshier,例如,C++ 允许为 NSPoint 定义一个 + 运算符。对我来说,p1 + p2 看起来比 NSOffsetPoint(p1, p2) 要好得多。在处理地图和列表等数据结构时,C++ 更加简洁(除非结果直接在 UI 中使用——这就是你应该使用 NS 类的地方)。接下来 - 数学任务。用于矩阵和向量的 C++ 库通常比 NSAffineTransform 和其他更容易使用。 NS 类在使用 NSGraphicsContext 时再次表现出色,但某些后端数学绝对应该使用 C++ 处理以简化代码。恕我直言,当然
猜你喜欢
  • 2013-11-17
  • 2020-06-20
  • 2015-11-16
  • 1970-01-01
  • 2019-02-14
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
相关资源
最近更新 更多