结尾:编程指南

结尾:编程指南

前言

一些有助于指导你进行低级程序设计和编写代码的建议。这些只是指导方针,而不是规则。我们的想法是将它们用作灵感,并记住偶尔会违反这些指导方针的特殊情况。

设计

  • 优雅总是会有回报

从短期来看,似乎需要更长的时间才能找到一个真正优雅的问题解决方案,但是当该解决方案第一次应用并能轻松适应新情况,而不需要数小时,数天或数月的挣扎时,你会看到奖励(即使没有人可以测量它们)。它不仅为你提供了一个更容易构建和调试的程序,而且它也更容易理解和维护,这也正是经济价值所在。这一点可以通过一些经验来理解,因为当你想要使一段代码变得优雅时,你可能看起来效率不是很高。抵制急于求成的冲动,它只会减慢你的速度。

  • 先让它工作,然后再让它变快

即使你确定一段代码非常重要并且它是你系统中的主要瓶颈**,也是如此。不要这样做。使用尽可能简单的设计使系统首先运行。然后如果速度不够快,请对其进行分析。你几乎总会发现“你的”瓶颈不是问题。节省时间,才是真正重要的东西。

  • 创建类时,给类起个清晰的名字,就算不需要注释也能理解这个类

你的目标应该是使客户端程序员的接口在概念上变得简单。为此,在适当时使用方法重载来创建直观,易用的接口。

  • 注意长参数列表

那样方法调用会变得难以编写,读取和维护。相反,尝试将方法移动到更合适的类,并且(或者)将对象作为参数传递。

  • Java命名约定

为了增加标识符的可读性,形式上采用大小写混合方式,为了避免使用长的名字,一般少于15个字母。另外少用或慎用缩写。保证整个程序中风格统一。

  • Java注释规则

声明需要考虑注释的地方是:类的功能和用处、类的开发和维护历史。接口需要注释的地方是:接口的用途、使用环境和方法。属性的注释需要对属性的描述。成员方法的注释:方法功能说明、方法参数说明、返回值说明,异常说明。

  • 不要重复开发

如果一段代码出现在派生类的许多方法中,则将该代码放入基类中的单个方法中,并从派生类方法中调用它。这样不仅可以节省代码空间,而且可以轻松地传播更改。有时,发现这个通用代码会为接口添加有价值的功能。此指南的更简单版本也可以在没有继承的情况下发生:如果类具有重复代码的方法,则将该重复代码放入一个公共方,法并在其他方法中调用它。

  • 从设计的角度,寻找和分离那些因不变的事物而改变的事物

也就是说,在不强制重新设计的情况下搜索可能想要更改的系统中的元素,然后将这些元素封装在类中。

  • 不要通过子类扩展基本功能
    如果一个接口元素对于类来说是必不可少的,则它应该在基类中,而不是在派生期间添加。如果要在继承期间添加方法,请考虑重新设计。

  • 遵循编码惯例

有很多不同的约定,例如,谷歌使用的约定。如果坚持使用其他语言的编码风格,那么读者就会很难去阅读。无论决定采用何种编码约定,都要确保它们在整个项目中保持一致。集成开发环境通常包含内置的重新格式化(reformatter)和检查器(checker)。

  • 无论使用何种编码风格,如果你的团队(甚至更好是公司)对其进行标准化,它就确实会产生重大影响

这意味着,如果不符合这个标准,那么每个人都认为修复别人的编码风格是公平的游戏。标准化的价值在于解析代码可以花费较少的脑力,因此可以更专注于代码的含义。

  • 遵循标准的大写规则

类名的第一个字母大写。字段,方法和对象(引用)的第一个字母应为小写。所有标识符应该将各个单词组合在一起,并将所有中间单词的首字母大写。例如:

  1. ThisIsAClassName
  2. thisIsAMethodOrFieldName

static final 类型的标识符的所有字母全部大写,并用下划线分隔各个单词,这些标识符在其定义中具有常量初始值。这表明它们是编译时常量。

  1. 包是一个特例,它们都是小写的字母,即使是中间词。域扩展(comorgnetedu等)也应该是小写的。这是Java 1.1Java 2之间的变化。
  • 不要创建自己的“装饰”私有字段名称

这通常以前置下划线和字符的形式出现。匈牙利命名法(译者注:一种命名规范,基本原则是:变量名=属性+类型+对象描述。Win32程序风格采用这种命名法,如WORD wParam1;LONG lParam2;HANDLE hInstance)是最糟糕的例子,你可以在其中附加额外字符用于指示数据类型,用途,位置等,就好像你正在编写汇编语言一样,编译器根本没有提供额外的帮助。这些符号令人困惑,难以阅读,并且难以执行和维护。让类和包来指定名称范围。如果认为必须装饰名称以防止混淆,那么代码就可能过于混乱,这应该被简化。

  • 在创建一般用途的类时,遵循“规范形式”

包括equals()hashCode()toString()clone()的定义(实现Cloneable,或选择其他一些对象复制方法,如序列化),并实现ComparableSerializable

  • 对读取和更改私有字段的方法使用“get”,“set”和“is”命名约定

这种做法不仅使类易于使用,而且也是命名这些方法的标准方法,因此读者更容易理解。

  • 有时需要继承才能访问基类的protected成员

这可能导致对多种基类型的感知需求(perceived need)。如果不需要向上转型,则可以首先派生一个新类来执行受保护的访问。然后把该新类作为使用它的任何类中的成员对象,以此来代替直接继承。

  • 不要成为过早优化的牺牲品

过早优化是很疯狂的行为。特别是,不要担心编写(或避免)本机方法(native methods),将某些方法设置为final,或者在首次构建系统时调整代码以使其高效。你的主要目标应该是验证设计。即使设计需要一定的效率,也先让它工作,然后再让它变快。

  • 保持作用域尽可能小,以便能见度和对象的寿命尽可能小

这减少了在错误的上下文中使用对象并隐藏了难以发现的bug的机会。例如,假设有一个集合和一段迭代它的代码。如果复制该代码以用于一个新集合,那么可能会意外地将旧集合的大小用作新集合的迭代上限。但是,如果旧集合比较大,则会在编译时捕获错误。

  • 使用标准Java库中的集合

熟练使用它们,将会大大提高工作效率。首选ArrayList用于序列,HashSet用于集合,HashMap用于关联数组,LinkedList用于堆栈(而不是Stack,尽管也可以创建一个适配器来提供堆栈接口)和队列。当使用前三个时,将其分别向上转型为ListSetMap,那么就可以根据需要轻松更改为其他实现。

  • 为使整个程序健壮,每个组件必须健壮

在所创建的每个类中,使用Java所提供的所有工具,如访问控制,异常,类型检查,同步等。这样,就可以在构建系统时安全地进入下一级抽象。

  • 编译时错误优于运行时错误

尝试尽可能在错误发生点处理错误。在最近的处理程序中尽其所能地捕获它能处理的所有异常。在当前层面处理所能处理的所有异常,如果解决不了,就重新抛出异常。

  • 注意长方法定义

方法应该是简短的功能单元,用于描述和实现类接口的离散部分。维护一个冗长而复杂的方法是很困难的,而且代价很大,并且这个方法可能是试图做了太多事情。如果看到这样的方法,这表明,至少应该将它分解为多种方法。也可能建议去创建一个新类。小的方法也可以促进类重用。(有时方法必须很大,但它们应该只做一件事。)

  • 保持“尽可能私有”

一旦公开了你的类库中的一个方面(一个方法,一个类,一个字段),你就永远无法把它拿回来。如果这样做,就将破坏某些人的现有代码,迫使他们重写和重新设计。如果你只公开了必须公开的内容,就可以轻易地改变其他一切,而不会对其他人造成影响,而且由于设计趋于发展,这是一个重要的自由。通过这种方式,更改具体实现将对派生类造成的影响最小。在处理多线程时,私有尤其重要,只有私有字段可以防止不同步使用。具有包访问权限的类应该仍然具有私有字段,但通常有必要提供包访问权限的方法而不是将它们公开。

  • 避免使用“魔法数字”

这些是指硬编码到代码中的数字。如果后续必须要更改它们,那将是一场噩梦,因为你永远不知道“100”是指“数组大小”还是“完全不同的东西”。相反,创建一个带有描述性名称的常量并在整个程序中使用常量标识符。这使程序更易于理解,更易于维护。

  • 在构造方法内部,只需要将对象设置为正确的状态

主动避免调用其他方法(final方法除外),因为这些方法可以被其他人覆盖,从而在构造期间产生意外结果。较小,较简单的构造方法不太可能抛出异常或导致问题。

  • 如果类在客户端程序员用完对象时需要进行任何清理

请将清理代码放在一个明确定义的方法中,并使用像 dispose() 这样的名称来清楚地表明其目的。另外,在类中放置一个 boolean 标志来指示是否调用了 dispose() ,因此 finalize() 可以检查“终止条件”

  • finalize() 的职责只能是验证对象的“终止条件”以进行调试

在特殊情况下,可能需要释放垃圾收集器无法释放的内存。因为可能无法为对象调用垃圾收集器,所以无法使用 finalize() 执行必要的清理。为此,必须创建自己的 dispose() 方法。在类的 finalize() 方法中,检查以确保对象已被清理,如果没有被清理,则抛出一个派生自RuntimeException的异常,以指示编程错误。在依赖这样的计划之前,请确保 finalize() 适用于你的系统。(可能需要调用 System.gc() 来确保此行为。)

如果必须在特定范围内清理对象(除了通过垃圾收集器),请使用以下准则: 初始化对象,如果成功,立即进入一个带有 finally 子句的 try 块,并在 finally中执行清理操作。

  • 继承期间覆盖 finalize() 时,记得调用 super.finalize()

(如果是直接继承自 Object 则不需要这样做。)调用 super.finalize() 作为重写的 finalize() 的最终行为而不是在第一行调用它,这样可以确保基类组件在需要时仍然有效。

  • 优先选择 接口 而不是 抽象类

如果知道某些东西应该是基类,那么第一选择应该是使其成为一个接口,并且只有在需要方法定义或成员变量时才将其更改为抽象类。一个接口关心客户端想要做什么,而一个类倾向于关注(或允许)实现细节。

  • 为了避免非常令人沮丧的经历,请确保类路径中的每个名称只对应一个不在包中的类

否则,编译器可以首先找到具有相同名称的其他类,并报告没有意义的错误消息。如果你怀疑自己有类路径问题,请尝试在类路径的每个起始点查找具有相同名称的 .class 文件。理想情况下,应该将所有类放在包中。

  • 请注意,相比于编写代码,代码被阅读的机会更多

清晰的设计可能产生易于理解的程序,但注释,详细解释,测试和示例是非常宝贵的,它们可以帮助你和你的所有后继者。如果不出意外,试图从JDK文档中找出有用信息的挫败感应该可以说服你。

结尾

市面上也有很多大厂的开发手册,比如:谷歌、阿里等大厂,推出的不同编程语言的开发理念。标准化开发总归是好的,当你接手他人代码时,你会发现标准化给你带来的好处巨大。

事在人为,作为一名程序员更多是要有责任心,比如:开发时,重要的地方写明注释;提交代码时一定要先拉取代码后再提交,很多时候就是因为代码被覆盖导致接口404;接口写完后一定要先简单的进行单元测试,并且编写接口文档供前端参考,我见过太多的开发,不做单元测试也不写接口文档,前端对接时,烦的想骂人;所以为了与你的同事能和平的团队协同,也为了方便后接手你代码的同事,把自己应作的做到尽职尽责。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值