Objective-C 项目重构利器:把项目中的导入依赖(Import Dependancies)图示化

简介: 作为开发者,我们都喜欢干净的代码,但实际上我们大部分时间都是和糟糕的代码打交道。这些代码可能是最近写的,也可能是遗留下来的,可能是我们自己写的,可能是其他开发者写的。我们能认出什么是糟糕的代码,因为我们有代码的嗅觉(code smells)。换句话说,关于代码质量的启发式提问。在这些中,我们可以命名我写在这里和这里的"死亡"的代码(dead code),也可以命名紧密耦合(tight coupling)。

代码设计和疏耦合(loose coupling)

作为开发者,我们都喜欢干净的代码,但实际上我们大部分时间都是和糟糕的代码打交道。这些代码可能是最近写的,也可能是遗留下来的,可能是我们自己写的,可能是其他开发者写的。我们能认出什么是糟糕的代码,因为我们有代码的嗅觉(codesmells)。换句话说,关于代码质量的启发式提问。在这些中,我们可以命名我写在这里这里的"死亡"的代码(dead code),也可以命名紧密耦合(tight coupling)。


紧密耦合是形容一个系统中,很多部件互相依赖。紧密耦合的代码令人讨厌,耦合的点上要考虑太多的关系,又或者这些耦合点横跨了几个不同的类,而不是单独有一个类。相反,疏耦合是一个更好的设计,可以提高单个耦合点的作用,和分散不同的关注点。疏耦合使代码更容易测试和维护。


在 Objective-C 中,可以通过 delegatesnotification来改善耦合程度。


类依赖的图示化

所以,我们怎么实现我们的代码的疏耦合?首先,我们需要更好地理解当前代码的耦合情况。让我们定义类依赖:A 类 依赖 B 类,如果 A 在头文件中导入(import) B 类。在这样的定义下,我们可以根据每个类的 #import 指令画一个类与类之间的依赖图。我们预设这里的文件名是根据它包含的类来命名的。


我写了 objc_dep.py,一个可以从 Objective-C 源代码中抽取 imports 信息的 Python 脚本。这个结果可以在 GraphVizOmniGraffle 上演示。这样,你就可以看到类与类之间依赖图示。注意我们也可以对耦合进行度量计算(compute metrics),但不是这里想要说的点。


用法举例

我们怎么从耦合图示中得出更好的设计?这里没有决定性的算法,而且也要根据你的项目而定。让我们试试把这个脚本用在 FSWalker 上,它是我很久以前写的一个小型 iPhone 文件浏览器。


1.产生图示

$ python objc_dep.py /path/to/FSWalker > fswalker.dot

2.在 OmniGraffle 打开它

在这里,我们可以看到以类为节点、通过线条连起来的依赖。

image.png

3.移除目录(categories)

我们可以安全地把 Objective-C 目录移除掉, 因为很多地方的目录引用(referencing categories)不是我们现在考虑的范围。

image.png

4.把相关的类分组

下一步,我们移动一下头部的节点,试着根据一些共同的依赖分组成不同的群集:

image.png

5.研究奇怪的依赖

图示现在给了我们一个全面的代码架构。控制器(controller)对象已经用红色标出, 模块(model)对象用黄色标出,网络的部分用蓝色标出。这样的图示可以令我们去发现奇怪的依赖,和思考代码的设计。我们可以首先注意到,FSWalkerAppDelegate 有太多的依赖。我们可以对这些依赖分来:


* 无引用的类和群集(unreferenced classes or clusters),这些是废弃的代码,我们可以移除它。不过这里没有无引用的类,尽管你可以轻易在一些大一点的项目中看到。

* 二路引用(two-ways references)

或者一个类不应该直接地引用另一个类,而应该引用别的类实现的协议。

我们这里有两个二路引用的例子:HTTPServer 和 HTTPConnection,RootViewController 和 FSWalkerAppDelegate。前者是 CocoaHTTPServer 项目的一部分,不是我们项目设计的问题(issue)。然而,后者是个问题。通过观察代码,我们会注意到,RootViewController 并没有真正使用 FSWalkerAppDelegate。这样的 import 可以安全地移除。


  • 奇怪的引用(weird references)
    一些 import 指令可能是不必要的,或者揭示设计问题。

并没有好的理由说明为什么 FSWalkerAppDelegate 要引用 FSItem,InfoPanelController也一样。代码检测会告诉我们,DetailViewController 和 InfoPanelController 不应该被 FSWalkerAppDelegate 引用,而应该被 RootViewController 引用。所以这个是最后的图示。FSWalker 的架构或者还需要优化,但你应该明白我的意思。

image.png

实际项目中的用法

这个是你可能设想到的带 100 多个类的项目的图示:

image.png

额外选项

% python objc_dep.py /path/to/repo -x"(^Internal|secret)" -i subdir1 subdir2 > graph.dot

上面会包括所有以 "Internal" 开头为名字的文件,或者包括包含 "secret" 这个词为名字的文件。所有在 subdir1 和 subdir2 目录下的文件会被忽略。

相关文章
|
程序员 开发工具 iOS开发
Objective C项目的命名有多长
Objective-C程序员一定非常喜欢它详细的描述性命名风格。
254 0
Objective C项目的命名有多长
|
JSON API iOS开发
Github上优秀的Objective-C项目简介
<p style="margin-top:0px; margin-bottom:1.1em; color:rgb(54,46,43); font-family:'microsoft yahei'; font-size:14px; line-height:26px"> Github上优秀的Objective-C项目简介</p> <p style="margin-top:0px; marg
2422 0
|
C语言 C++ iOS开发
【《Objective-C基础教程 》笔记ch02】(一)Hello Object-C 项目
一、项目实现步骤。 1、若xcode尚未运行,先启动它。 2、选择Select File --> New --> New Project菜单选项。 3、选择左边OS X下得application,再选中右边的command line tool,点击next。
1021 0
|
5月前
|
安全 编译器 Swift
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
317 2
|
3月前
|
开发工具 iOS开发 容器
【Azure Blob】关闭Blob 匿名访问,iOS Objective-C SDK连接Storage Account报错
iOS Objective-C 应用连接Azure Storage时,若不关闭账号的匿名访问,程序能正常运行。但关闭匿名访问后,上传到容器时会出现错误:“Public access is not permitted”。解决方法是将创建容器时的公共访问类型从`AZSContainerPublicAccessTypeContainer`改为`AZSContainerPublicAccessTypeOff`,以确保通过授权请求访问。
【Azure Blob】关闭Blob 匿名访问,iOS Objective-C SDK连接Storage Account报错
|
5月前
|
缓存 开发工具 iOS开发
优化iOS中Objective-C代码调起支付流程的速度
优化iOS中Objective-C代码调起支付流程的速度
70 2
|
5月前
|
安全 JavaScript 前端开发
IOS开发基础知识:介绍一下 Swift 和 Objective-C,它们之间有什么区别?
IOS开发基础知识:介绍一下 Swift 和 Objective-C,它们之间有什么区别?
205 0
|
iOS开发 容器
iOS 代码规范格式 Objective-C(上)
iOS 代码规范格式 Objective-C
422 0
iOS 代码规范格式 Objective-C(上)