按功能建立包结构(Package)

陈述观点:按功能建立包结构,而不是按层级建立。

导读:有人喜欢把 Activity 充当 Controller 使用,也有人喜欢使其扮演 View 角色。这个问题本文不作讨论。所以在本文没有 Activity 的存在,只有 Controller and View。

一般我们建立包结构的方式有两种:

1、按层级建立包结构

如下图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
├── controller
│   ├── LoginController.java
│   ├── NewsController.java
│   └── RegisterController.java
├── customView
│   ├── CustomEditText.java
│   ├── CustomTextView.java
│   └── FloatingTextView.java
├── db
│   └── DBHelper.java
├── model
│   ├── News.java
│   └── User.java
├── network
│   └── NetworkHelper.java
├── utils
│   └── BitmapUtil.java
└── view
    ├── LoginView.java
    ├── NewsView.java
    └── RegisterView.java

简单列举大概结构,每个项目的结构都不一样。

db、 network、utils 这类可公用的抽出来单独放,OK,没问题。

model 有些同学会分 ui model、network model、db model,然后再用 mapper 去转换。分法因人、项目而异。model 里的数据结构类经常是很多地方都会使用到的,例如 UserInfo,几乎整个项目、每个页面都需要用到,所以 model 建议也是统一抽出来放。

重点,现在问题来了

有些同学喜欢把 Controller or Presenter 放一堆,ui view 放一堆,自定义的 view(无论复不服用)放一堆,如上图。

也许上图的项目功能甚少,看上去感觉没问题,甚至还觉得挺合理。

现在我们把问题放大一点,假设项目越来越庞大,ui 界面有 30 个,那么一个 Controller 包下就有 30 个 Controller,一个 view 包下有 30 个 ui view。

1、如果想快速找到忘记了名字的类,那就坑爹了。(这事我遇过)

也许你会说,我都记得住他们叫什么名字呀,command + O 就出来了 ^_^

2、如果是找页面里的一个辅助类、抽象类 or 其他呢? 还记得住吗?

例如,有一个页面抽象类,分别有两个页面继承它,毫无疑问它是属于 view 包的,但把它放到 view 包,也毫无疑问是给这个包带来了杂乱的信息。也给上面的情况(忘记了类的名字)带来更麻烦的查找(因为你要用肉眼遍历一次整个包呀亲)。

3、而如果按层级建立的包结构,protected 关键字会失效,准确来说是“相对失效”。

例如,LoginView 的一些方法我只想让 LoginController 调用,所以我会用 protected 关键字修饰。此时,如果是按层级建立的包结构,处于与 LoginView 不同包的 LoginController 则无法调用 LoginView 用 protected 关键字修饰的方法。

按层级建立的包结构,除了能给别人对你的项目架构一目了然之外,也没啥好处了。然而,这个“一目了然”也是没有存在的必要。需要急着告诉别人么?所以说白了,就是没啥用。

2、 按功能建立包结构

如图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
├── customView
│   ├── CustomEditText.java
│   └── CustomTextView.java
├── db
│   └── DBHelper.java
├── features
│   ├── login
│   │   ├── LoginController.java
│   │   └── LoginView.java
│   ├── news
│   │   ├── FloatingTextView.java
│   │   ├── NewsController.java
│   │   └── NewsView.java
│   └── register
│       ├── RegisterController.java
│       └── RegisterView.java
├── model
│   ├── News.java
│   └── User.java
├── network
│   └── NetworkHelper.java
└── utils
    └── BitmapUtil.java

按照功能来分包结构就会清晰多了,继续沿用上述假设的条件,我们的 APP 有 30 个页面。那我们会分 30 个功能包?

答案是 NO!注意我们是按功能分,而不是按页面分。而一个功能是由几个页面组成这种事情也是常见的。

例如信息编辑页面,信息编辑页面里都会集成一些二级页面,甚至三级页面,如填写描述、选择分类、标签等等等。

所以一个编辑功能,就会有 4-5 个页面(具体看产品设计)。30 个页面分摊下来,也许就是 7-9 个功能,也就是说会分出来 7-9 个功能包。

“按层级建立包结构”的问题在“按功能建立的包结构”的表现:

1、在忘记 X 类名字的情况下查找:

遍历 7-9 个元素比遍历 30 个元素快吧?找到对应的包,就一目了然了。

2、辅助类、抽象类、XX 类的存放

都已经按功能分了,直接放一起就好

3、使用 protected 关键字修饰方法

同一个功能的类都在同一个包,使用 protected 修饰仅供本包类调用的方法。完全符合设计规范!

在曾经的一个团队里,我们有个默认的规矩,就是我做的功能(我们按功能分工),别的小伙伴不能动、不能改。也就是说,我新建的包(仅对功能包而言,工具等例外),有需要就要和我沟通,我亲自改或提供接口给其调用。

对小团队开发还是挺管用的,几十号人的庞大开发队伍就忽略吧。

你有更好用的方法吗? 分享分享,我们讨论讨论?

参考文献:Package by features, not layers