读过spring源码的同学一定还记得spring中的核心方法—— refresh() , 它用十几行代码为我们描述了spring容器的启动过程:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
} catch (BeansException ex) {
destroyBeans();
cancelRefresh(ex);
throw ex;
} finally {
resetCommonCaches();
contextRefresh.end();
}
}
}
2、函数
2.1 短小
函数的第一规则是要短小,第二规则是要更短小。
2.2 只做一件事
以文章开头提到的spring源码中,refresh方法为例, 这个方法做的事情就是刷新容器,然后在这个抽象层级下面,有分为多个抽象层级,如: 预刷新、获取beanFactory、beanFactory预处理等。
同样,我们平常写业务接口时,一个接口实现一个功能,但是每个功能下又可以拆分为多个不同的子功能,将每个子功能抽象成一个函数,而不要将所有的业务逻辑堆积在主函数中,使得主函数很臃肿。
2.3 每个函数一个抽象层级
要确保函数只做一件事,函数中的语句要在同一个抽象层级上。
函数中混杂不同抽象层级,阅读代码时很难判断某个表达式是基础概念还是细节。而且,就像破窗理论,一旦细节与基础概念混杂,后面的迭代就会将更多的细节放入函数中。
这条规则比较理想化,实际应用中很难写出只停留在一个抽象层级上的函数。但这条规则可以让函数短小、确保函数只做一件事。
2.4 switch语句
switch的出现就是为了完成多件事,所以使用它的时候很难做到上面说的:短小、只做一件事。这种情况下一般可以考虑使用设计模式或者使用java中的Enum来代替switch。
2.5 使用描述性的名称
这一点与上一篇文章中提到的——有意义的命名,都是为了让读者看到代码时能见名知意。
2.6 函数参数
函数的参数越少越好。
2.7 无副作用
2.8 分隔指令与询问
2.9 使用异常代替返回错误码
2.10 别重复自己
对于在多个地方使用的业务模块要抽取出来。一来方便代码阅读,二来如果需要修改业务逻辑,只需要修改一处即可。
2.11结构化编程
2.12 如何写出这样的函数
写代码和写文章一样,要先想好要写什么,写完后还要去打磨它。 初稿或许粗陋无序,你要认真推敲,知道达到你想要的样子。
2.13 小结
每个系统都是使用某种领域特定语言搭建,而这种语言是程序员设计来描述那个系统的。函数是语言的动词,类是名词。 这并非单纯指需求文档中的名词和动词就是系统中类与函数的最初设想。编程艺术是且一致就是语言设计的艺术。
好的程序员将系统当做故事来写,而不是当做程序来写。他们使编程语言变为一种更为丰富且更具表达力的语言。
人都是惰性的,很多时候我们写完代码只是为了实现业务需求,功能实现了就行了,而不关心代码的是否优雅、是否可读。
3、类
3.1 类的组织
遵守标准的Java约定, 类应该从一组变量列表开始,如果有公共静态常量,应该先出现,然后是私有静态变量、私有实体变量, 很少会有公共变量。
公共函数应该在变量列表之后。
封装:
我们习惯于保持变量和工具函数的私有性,但并不执着于此。有时也会用到受保护的变量或工具函数,方便测试访问到。
3.2 类应该短小
这一点与函数相似但也有不同。类的名称应该描述其职责。如果一个类无法找一个合适名称描述其职责,那这个类大概就太长了。类名越含混,这个类越有可能拥有过多职责。
3.2.1 单只职责原则
单一职责原则认为, 类或模块应该有且只有一条加以修改的理由。
3.2.2 内聚
内聚:表示内部间聚集、关联的程度。 增强内聚度的方法:
- 模块只对外暴露最小限度的接口,形成最低的依赖关系。
- 只要对外接口不变,模块内部的修改,就不得影响其他模块。
- 删除一个模块,应当只影响有依赖关系的其他模块,而不应该影响其他无关部分。
3.2.3 保持内聚性就会得到很多短小的类
3.3 为了修改而组织
对于多数系统,修改将一直持续。没出修改都让我们冒着系统其它部分不能如期工作的风险。在整洁的系统中,我们对类加以组织,降低修改的风险。
隔离修改:
需求会改变,所以代码也会改变。抽象类一般用于呈现概念,具体类包含实现细节。当细节改变时,依赖于具体细节的客户类就会有风险。我们可以借助接口和抽象类来隔离这些细节带来的影响。