目录

1、整洁代码

2、有意义的命名

3、函数:

4、注释

5、格式

纵向格式:

横向格式:

团队规则:

6、对象和数据结构

7、错误处理

8、边界

9、单元测试

10、类

11、系统

12、迭进

13、并发编程

14、逐步改进


1、整洁代码

1)要有代码:因为代码呈现需求的细节,而这些细节无法被忽略或者抽象,因此需要规范。

2)糟糕代码:糟糕的代码就像沼泽一样,曾经说过有朝一日再回头清理——稍后等于永不

3)混乱代价:随着时间发展,生产力会随着糟糕的代码趋向于零

4)新设计:当旧系统糟糕到开发团队造反,重新开发新系统替代时候,新系统不久也会走老系统的老路。

5)态度:程序员遵从 不了解混乱风险的经理的意愿是不专业的做法,就像医生做手术按照病人说的来办。

6)谜底:制造混乱无助于赶上期限,赶上期限的唯一方法是:始终尽可能保持代码整洁

7)整洁代码的艺术:像绘画,需要遵循大量小技巧,习得“整洁感”和“代码感”。

8)什么是整洁代码

Bjarne Stroustrup(C++语言发明者):我喜欢优雅和高效的代码,代码逻辑应当直截了当,叫缺陷难以隐藏;尽量减少依赖关系,使之便于维护;依据某种分层战略完善错误处理代码;性能调至最优,省的引诱别人做没规矩的优化,搞出一堆混乱来。——整洁的代码只做好一件事

 Grady Booch(《面向对象分析与设计》作者):整洁的代码简单直接。整洁的代码如同优美的散文,整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直接了当的控制语句。

Michael Feathers(《修改代码的艺术》作者):整洁的代码总是看起来像是某位特别在意的人写的,几乎没有改进的余地,代码作者什么都想到了,如果你企图改进它,总会回到原点,赞叹某人留给你的代码——全心投入的某人留下的代码

Ron Jeffries(《极限编程实践》作者): <1>能通过所有测试;<2>没有重复代码;<3>体现系统中的全部设计理念;<4>包括尽量少的实体,比如类、方法、函数等。——有意义的命名;消除重复代码;只做一件事;表达力;小规模抽象

Ward Cunningham(Wiki发明者,极限编程创始人之一):如果每个例程都让你感到深合己意,那就是整洁代码。如果代码让编程语言看起来像是专门解决那个问题而存在,就可以成为漂亮代码。——让编程语言像是专为解决那个问题而存在

本书中设计原则引用:单一职责原则(SRP)开放闭合原则(OCP)依赖倒置原则(DIP)

2、有意义的命名

1)名副其实:变量、函数或类的名称应该回答为什么存在、做什么事情、怎么用等问题。

2)避免误导:应该避免使用与本意相悖的词。hp、aix、sco都不应该作为变量名,因为他们都是UNIX平台或类UNIX平台的专有名称。避免小写字母l和字母o作为变量名因为在某些字体下和1(一)以及0(零)太像。

3)做有意义的区分:以数字系列命名(a1、a2,...aN)没有提供正确信息,没有提供作者意图。废话也是无意义的表达,需要区分名称。

4)使用读的出来的名称:人类擅长记忆和使用单词,命名读起来顺口较好。

5)使用可以搜索的名称:单个字符a或数字常量7不太容易搜索,因此使用单词或MAX_CLASSES_PER_STUDENT更利于搜索。

6)避免使用编码:编码太多了,不应该增加解码负担。

7)避免思维映射:循环计数器常用i、j、k来计数,使用a,b、c不太好。——明确是王道

8)类名和方法名:类名和对象名应该是名词或名词短语;方法名应该为动词或动词短语。

9)别用双关语:代码作者应该写出易于理解的代码。

10)使用有意义的语境:添加前缀addFirstName、addLastName、addState等,不要添加没有用的语境,添加IDE不能自动添加的语境就是浪费生命。

3、函数:

错误函数写法:一个函数中包含多个不同层级的抽象,奇怪的字符串处理和函数调用,混用双重嵌套以及多个if语句等。

推荐函数写法:

1)短小:20行封顶最佳,不应该超过100行(一屏幕显示长度)。——if语句、else语句、while语句等,代码块应该只有一行,该行为一个函数调用语句

2)只做一件事:做好这件事,只做一件事情;包括:同一抽象层上的步骤。

3)每个函数一个抽象层级:自顶向下读代码(向下规则),一个函数相当于文章中一段,每段描述当前抽象层级,并引出下一抽象层级。

4)短小的switch语句:若遇到switch根据类型,new新的对象时候,可以将switch埋到抽象工厂底下,利用接口多态的new新的对象。

5)使用描述性的名称:长而有描述性的名称比短而令人费解的名称好;选择描述性的名称能帮助理清模块的设计思路,并帮助改进思路。

6)函数参数:最好是参数为0,其次是一个,两个就可能造成顺序问题。三个及以上最好使用结构体或类缩短参数个数;不要在参数列表地方弄输出参数,很容易出错。

7)使用异常代替返回错误码:错误码会带来更深的嵌套结构,异常能简化代码结构。函数应该只做一件事情,而错误处理就是一件事,因此错误处理try语句不应在函数中出现。应该让函数抛出异常。

8)不要重复自己:重复代码应该抽离出来,不然修改时候需要修改多个地方。

9)如何写出这样函数:写代码应该像写文章或写论文一样,先想到什么就写什么,然后再打磨他。

4、注释

1)能用代码表达意思的用代码表达:注释的问题在于:代码在不断更新,注释很少有人维护

2)好的注释:法律信息、对正则表达式的解释、警告信息、TODO工作列表、放大不合理地方的重要性。

3)坏的注释:不明所以、多余的注释、误导性注释、循规式注释(每个函数,每个参数都有注释是愚蠢可笑的)、日志式注释、废话注释、位置标记、括号后面的注释、归属或署名、注释掉的代码(应该删除,版本控制系统可以帮助找回历史代码)、HTML注释、非本地信息、信息过多、不明显的联系、函数头(利用好的函数名字解决)、过长的注释(读起来浪费时间)。

5、格式

格式的目的:代码格式关乎沟通,沟通是开发者的头等大事。代码阅读一般都是从上往下,从左往右,代码行应展示一条完整的思路。

纵向格式

1)垂直方向上的分割:利用空行分割垂直方向上的代码,因为目光总会停留在空白行之后那一行。

2)垂直方向上的靠近:有密切关系的代码行应相互靠近,例如相关变量声明应放在一起。

3)变量声明:应尽可能靠近其使用位置,本地变量应放在函数的顶部。

4)实体变量:C++习惯放在底部,java习惯放在顶部。重点是放在谁都知道的地方声明,尊重习惯。

5)相关函数:应该按照调用的顺序,从上往下的自然顺序。

6)概念相关:概念相关的代码应该放在一起。相关性越强,彼此之间的距离就应该越短。

横向格式:

1)一行长度:循序无需拖动滑动条到右边的原则,一般在100字符以内。

2)水平间隔和靠近:使用空格字符将彼此紧密相关的事物连接在一起,也可以使用空格把相关性较弱的事物分隔开。人第一眼比较容易看到空格后一个字符。在赋值操作符周围加空格以此达到强调作用;不在函数名和左括号之间加空格,因为函数和参数密切相关;参数一 一隔开,使用逗号强调参数是分离的。

3)水平对齐:变量水平对齐没有啥用,有自动格式化工具使用自动格式化工具对齐即可。

4)缩进:花括号缩进有利于查看代码工作范围。没有缩进的代码难以阅读。

5)空范围:while或for语句的语句体为空时候,应该确保分号在下一行缩进位置。或者使用花括号。

团队规则:

1)每个人都有自己喜欢的格式规则,但若在一个团队中工作,应该统一使用一种格式风格

2)好的软件系统应该读起来是一致和顺畅的风格。

3)该书作者风格:见CodeAnalyzer.java

6、对象和数据结构

1)对象:把数据藏于抽象之后,暴露操作数据的函数;数据结构:直接暴露数据,没有提供有意义的函数。

2)过程式代码(使用数据结构):便于在不改动数据结构的前提下添加新函数;面向对象代码:便于在不改动既有函数的前提下添加新类。

3)迪米特法则:只和朋友谈话,不与陌生人谈话。类C的方法f只应该调用以下对象:C、由f创建的对象、作为参数传递给f的对象、由C的实体变量持有的对象。不应该调用任何函数返回的对象的方法。

4)数据传送对象(DTO):类似于java bean结构,对bean结构进行半封装,只暴露get接口。

7、错误处理

1)使用异常而非返回值:每调用一个函数之后,就得判断该函数返回值是否有问题的方式,搞乱了代码逻辑,而且这个判断很容易忘记没写。遇到错误时,最好跑出一个异常,这样调用代码就很整洁,逻辑不会被错误处理搞乱。见下图:DeviceController.java

//使用返回值
public class DeviceController {
...
public void sendShutDown() {DeviceHandle handle = getHandle(DEV1);if(handle != DeviceHandle.INVALID) {DeviceRecord record = retrieveDeviceRecord(handle);if(record.getStatus() != DEVICE_SUSPENDED) {pauseDevice(handle);clearDeviceWorkQueue(handle);closeDevice(handle);} else {logger.log("Device suspended. Unable to shut down");}} else {logger.log("Invalid handle for : " + id.toString());}}
}//使用异常代替返回值
public class DeviceController {
...
public void sendShutDown() {try {tryToShutDown();} catch(DeviceShutDownError e) {logger.log(e);}}private void tryToShutDown() throws DeviceShutDownError {DeviceHandle handle = getHandle(DEV1);DeviceRecord record = retrieveDeviceRecord(handle);pauseDevice(handle);clearDeviceWorkQueue(handle);closeDevice(handle);}private DeviceHandle getHandle(DeviceID id) {
...throw new DeviceShutDownError("Invalid handle for : " + id.toString());
...}
}

2)使用不可控异常:使用可控异常必须在catch语句和抛出异常处之间的每个方法签名中声明该异常。这意味着较低层级的修改会波及到较高层次的签名,破坏了封装。

3)给出异常发生的环境说明:可以从异常那里得到堆栈踪迹stack trace,从而了解异常的原因。

4)依调用者需要定义异常类:我们应用程序定义异常类时候,最重要考虑是:他们如何被捕获

5)特例模式:创建一个类或配置对象,用来处理特例。你来处理特例,客户代码就不用应付异常行为了,异常行为被封装到特例对象中。

//原代码
try {MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());m_total += expenses.getTotal();
} catch(MealExpensesNotFound e) {m_total += getMealPerDiem();
}//使用特例模式
MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();public class PerDiemMealExpenses impleMents MealExpenses {public int getTotal() {//return the per diem default}
}

6)别返回null值:每行代码都在检查null值的应用程序糟糕透了,只要有一处没有检查null值,程序就会失控。

//原代码
List<Employee> employees = getEmployees();
if(employees != null) {for(Employee e : employees) {totalPay += e.getPay();}
}//修改后
List<Employee> employees = getEmployees();
for(Employee e : employees) {totalPay += e.getPay();
}
//修改getEmployees方法,特殊情况返回空列表
public List<employee> getEmployees() {if( ... there are no employees ...)return Collections.emptyList();
}

7)别传递null值:当有人传递null值参数给一个函数,改如何处理,可以创建一个新异常类型并抛出;使用assert断言;但这两种方法均未解决问题,运行时候仍会错误。恰当的做法是禁止传入null值

8、边界

1)使用第三方代码:第三方库追求普适性,而使用者想集中满足特定需求的接口,这种差异可能导致系统边界上出现问题。例如java.util.Map中提供丰富接口,而我们希望使用者不要删除Map中的任何东西,但是java.util.Map中提供了clear接口。较好的方法是:使用Sensors类封装Map,隐藏细节,不暴露clear接口。——不建议总是用这种方式封装Map使用,而是建议不要将Map在系统中传递,当Map在这种边界时候,使用封装的方式更好

2)整洁的边界:边界上的代码需要清晰的分割和定义了期望的测试。应该避免我们的代码过多的了解第三方代码中的特定信息。依靠你能控制的东西,好过依靠你控制不了的东西,免得日后受它控制。

9、单元测试

1)TDD(测试驱动开发)三定律

定律一:测试先行(在写功能代码之前,先写测试用例代码);

定律二:只写刚好无法通过的单元测试,不能编译也算不通过。

定律三:只写刚好通过当前失败测试用例的生产代码。

这三条定律将限制你在大概30秒的一个循环中。测试与生产代码一起编写,测试只比生产代码早些几秒钟。

2)保持测试整洁

测试代码应该与生产代码一样重要,需执行同样的质量标准,脏测试等同于没有测试。测试代码必须随着生产代码的演进而修改。测试代码需要被思考、被设计、被照料,应该像生产代码一样保持整洁。

3)整洁的测试三要素:可读性、可读性、可读性。单元测试用例中需要呈现:构造——操作——检验(Build——Operate——Check)模式。避免用细节误导或吓到使用者。

4)双重标准:测试代码在不考虑性能和内存的情况下,可以和生成代码不同,但是测试代码应当简单、精悍、足具表达力。

5)每个测试一个概念:每个测试一个断言是个好准则,但是每个测试只测试一个概念更好。

6)FIRST:整洁测试5条规则:快速(Fast)、独立(Independent)、可重复(Repeatable)、自足验证(Self-Validating)、及时(Timely)。

10、类

1)类应该短小类的名称应该描述其职责,如果无法为某个类命以精确的名称,则这个类大概率太长了。类名中包括模糊的词:如Processor或Manager或Super等往往说明职责不清。

2)单一职责原则(SRP):类或模块应有且只有一条需要修改的理由。这句话说明了类的长度限制。

3)内聚:类应该只有少量实体变量,类中的每个方法都应该操作一个或多个这种变量。方法操作的变量越多,就越黏聚到类上。因此尽量保持方法和实体变量的粘性,若无法保证,则应该将这些变量和方法拆分到两个或多个类中,让新的类更为内聚。

4)为了修改而组织:对于多数系统,修改将一直持续。每一处的修改都冒着其他部分不能如期工作的风险。整洁系统中应该避免"打开"类进行修改,应该拓展系统而不是修改原有代码

5)隔离修改:需求会改变,因此代码也会改变。具体类包含实现细节,而抽象类只呈现概念。需求改变往往会带来细节的改变,因此我们应该借助接口和抽象类隔离这些细节

6)依赖倒置原则(DIP)应该依赖于抽象而不是依赖于具体的细节。

11、系统

1)将系统的构造与使用分开构造使用是非常不一样的过程。例如如下典型场景:就是所谓的“延迟初始化”,有一点好处是启动时间会更短,还能保证永远不会返回null值。

public Service getService() {if(service == null)service = new MyServiceImpl(...);return service;
}

然而,我们也得到了 MyserviceImpl 及其构造器所需的一切的硬编码依赖。不分解这些依赖关系就无法编译。如果 MyserviceImpl 是个重型对象,则测试也会是个问题

当然,仅出现一次的延迟初始化不算什么严重问题。但应用程序中往往许多类似的情况出现,我们不应该让这种小技巧影响整个系统设计。

2)分解main:将构造和使用分开的方法之一就是:将全部构造过程迁移到main或称为main的模块中。设计系统其余部分时候,假设所有对象都已经正确构造和设置。

3)依赖注入:对象不应负责实体化对自身的依赖,应将这个职责交给其他有权利的机制,从而实现控制的反转。

4)扩容:我们应该只去实现今天的用户故事,然后重构,明天在扩展系统实现新的用户故事。这是迭代增量敏捷的精髓所在。

5)测试驱动系统架构:软件设计师,不需要像建筑设计师一样 "先做大设计(Big Design Up Front--BDUF)"。实际上对于软件设计师,BFUF是有害的,它会阻碍改进,因为架构上的方案会影响后续的设计思路。

6)优化决策:在巨大的系统中,不管是一座城市或一个软件项目,无人能做所有决策。最好是授权给最优资格的人决策,但是延迟决策至最后一刻也是好手段。如果决策太早,会缺少客户反馈、关于项目的思考和实施经验。

7)系统需要领域特定语言:在软件领域,领域特定语言(DSL)是一种段杜的小型脚本语言或以标准语言写的API,领域专家可以用它编写读起来像组织严谨的散文一般的代码。

12、迭进

1)“简单设计”的四条规则,一下规则按重要程度排列:

运行所有测试:只要系统可测试,就会导向保持类短小且目的单一的设计方案。测试消除了对清理代码就会破坏代码的恐惧。

不可重复:重复是拥有良好设计系统的大敌,“小规模复用”可大量降低系统复杂性,想要实现大规模复用,必须理解如何实现小规模复用。

表达了程序员的意图(表达力);软件项目的主要成本在于长期维护。代码应该清晰地表达作者的意图,作者把代码写的越清晰,其他人花在理解代码上的时间就越少,从而减少缺陷,缩减维护成本。

尽可能减少类和方法的数量;类和方法数量太多,有时是由毫无意义的教条主义导致。我们的目标是在保持函数和类短小的同时,保持整个系统的短小精悍。

13、并发编程

1)为什么要并发:并发是一种解耦策略,它帮助我们把做什么(目的)何时做(时机)分解开。解耦目的和时机明显的改进应用程序的吞吐量结构

2)迷思与误解

对并发的误解:

<1>并发总能改进性能

<2>编写并发程序无需修改设计

<3>在采用Web或EJB容器时候,理解并发问题并不重要。

对并发中肯的说法:

<1>并发会在性能和编写额外代码上增加一些开销

        <2>正确的并发是复杂的,即便对于简单问题也是如此

<3>并发缺陷并非总能复现,所以常被看做偶发事件而忽略,未被当作真的缺陷看待

<4>并发常常需要对设计策略的根本性修改

3)并发防御原则

单一职责原则(SRP):方法、类、组件应当只有一个修改的理由。并发设计自身足够复杂到成为修改的理由。

限制数据作用域:应该谨记数据封装,严格限制对可能被共享的数据的访问。

线程应尽可能地独立:让每个线程在自己的世界中存在,不与其他线程共享数据。

4)了解执行模型

生产者—消费者模型:一个或多个生产者线程创建某些工作,并置于缓存或队列中。一个或多个消费者线程从队列中获取并完成这些工作。生产者和消费者之间的队列是一种限定资源

读者——作者模型:当存在一个主要为读者线程提供信息源,但只偶尔被作者线程更新的共享资源,吞吐量就会是个问题。挑战之处在于平衡读者线程和作者线程的需求,实现正确操作,提供合理的吞吐量,避免线程饥饿。

宴席哲学家:一群哲学家坐在圆桌旁,每个哲学家的左手边放了一把叉子。桌面中央摆着一大碗面。哲学家们思索良久,直至肚子饿了。每个人都要拿起叉子吃饭。但除非手上有两把叉子,否则就无法进食。

5)警惕同步方法之间的依赖:建议使用一个共享对象的多个方法。

6)保持同步区域微小:锁是昂贵的,应该尽可能少涉及临界区,尽可能减少同步区域。

7)很难编写正确的关闭代码:尽早考虑关闭问题,尽早令其工作正常,平静关闭很难做到。

8)测试线程代码:将伪失败(偶发事件)看做可能的线程问题;先使非线程代码可工作;编写可插拔的线程代码;编写可调整的线程代码;运行多于处理器数量的线程;在不同平台上运行;调整代码并强迫错误发生。

14、逐步改进

1)草稿:要编写整洁代码,必须先写肮脏代码,然后再清理它。

2)详细例子见原书籍《代码整洁之道》。

代码整洁之道-读书笔记相关推荐

  1. 代码整洁之道读书笔记——第一章:整洁代码

    软件质量,不仅仅依赖于项目架构和项目管理,同样重要的是代码质量!!! 序 神在细节之中,其实干什么事都一样,从小到大,一直明白一个道理:细节决定成败! 软件架构在开发中占据重要地位.其次,宏达建筑的最 ...

  2. 代码整洁之道-读书笔记之整洁的代码

    1.整洁代码 阅读本书有两个原因,第一,你是个程序员,第二,你想成为更好的程序员 1.1 要有代码 有人认为随着时代的发展,写代码不再是问题,我们更应该关注建模和需求 这句话后半句没有问题,因为语言在 ...

  3. 代码整洁之道 读书笔记

    第1章 整洁代码 1.1 要有代码 1.2 糟糕的代码      稍后等于永不 1.3 混乱的代价 假设前期不注意.后期的加入代码.改动效率都很低 1.3.1 华丽新设计 1.3.2 态度 1.3.3 ...

  4. 代码整洁之道-读书笔记1

    第一章 整洁代码 1.2糟糕的代码 糟糕的代码会毁掉一个公司,但是为什么会出现糟糕的代码? 可能是因为赶时间,如果花时间重构或者清理以前的代码,老板就会大发雷霆. 勒布朗法则:稍后等于用不. 1.3混 ...

  5. 代码整洁之道读书笔记(Ch4-Ch7)

    这几章从注释.程序格式.对象与数据结构的规范以及错误处理四个方面介绍了如何使代码变得简洁易懂.不同于上次摘抄的方法,这一次我会结合第一次个人作业的代码进行分析. 第四章  注释 这一章告诉我们,好的注 ...

  6. 代码整洁之道----读书笔记

    一.有意义的命名规则 二.优雅的函数 三.良好的注释 四.整齐的格式 转载于:https://www.cnblogs.com/k5bg/p/11063235.html

  7. 什么是好代码-代码整洁之道阅读笔记

    根据我所阅读的书<代码整洁之道>里的一句话: "衡量代码质量的唯一有效标准: WTF/min" 从哲学的角度讲,不得不说这真的很客观!!! 毕业不久的我也没有太多关于好 ...

  8. 不能将紧实的字段 绑定到_代码整洁之道【笔记】

    一.整洁代码 A.混乱的代价 1.有些团队在项目初期进展迅速,但有那么一两年的时间却慢去蜗行.对代码的每次修改都影响到其他两三处代码 2.花时间保持代码整洁不但有关效率,还有关生存 3.程序员遵从不了 ...

  9. 写好代码的注意点(代码整洁之道学习笔记)

    原则 写代码时注意,写好代码后,优化优化优化.重构重构重构 稍后等于永不(Later equals never) 命名 名副其实 避免误导 做有意义的区分 使用读的出来的名称 使用可搜索的名称 避免使 ...

  10. 软件架构整洁之道-读书笔记(3)

    第五部分:软件架构 第十五章:什么是软件架构 1.架构师是什么样的人? 首先软件架构师必须是能力最强的一群程序员,他们的代码产量可能不是最多的,但是他们必须不停的承接编程任务.如果不亲自承受因系统设计 ...

最新文章

  1. 阿里问题定位神器 Arthas 的骚操作,定位线上BUG,超给力
  2. Android webView 支持缩放及自适应屏幕
  3. JAVA spring配置文件总结
  4. 如何禁止使用bottomsheetdialogfragment拖动?
  5. 海报PSD分层促销模板|深层剖析设计套路
  6. springboot 常用插件
  7. linux计划任务一小时,linux,计划任务,每小时执行一次(共7篇).docx
  8. 大数据面试3分钟自我介绍_大数据开发工程师面试主要面试内容
  9. 关于appium环境搭建
  10. python-selenium 自动化弹幕
  11. 计算机竞赛 自主招生,想参加自主招生,五大学科竞赛如何选取?
  12. iOS3DTouch功能实现
  13. Python实例之利用h5py库保存数据集
  14. 1. 不吹不擂,第一篇就能提升你对Bean Validation数据校验的认知
  15. kaldi教程_赠书 | 全球稀缺的Kaldi学习资料,《Kaldi语音识别实战》给补上了
  16. 怎么实现CorelDRAW中轮廓图工具的快速运用
  17. python 爱心文字墙_博客园墙裂推荐!从未见过如些清新脱俗的完整Python+requests接口自动化测试框架搭建文章!...
  18. 牛客2019跨年AK场
  19. 大学生必须掌握的计算机软件基础
  20. xampp mysql 卸载_XAMPP怎么卸载

热门文章

  1. win10修复计算机是哪个键,教你Win10修复系统引导文件的详细方法
  2. 萤火虫小程序_漫展情报蛋趣携福利来萤火虫IDO漫展咯
  3. cocos2dx 组件
  4. 迅雷有linux版本吗,迅雷 - Linux Wiki
  5. 记录贴:阿里云 ECS服务器CentOS系统 搭建 Hexo 博客详细教程
  6. 手游最佳搭档:高续航音质卓越,高颜值精品蓝牙耳机推荐
  7. 组合逻辑与时序逻辑的区别
  8. Neo4j 下载安装
  9. 单片机 STM32 HAL IO扩展 74HC595 例子代码
  10. MapGuide open source开发心得二: 资源