优秀程序员都在注意的十个点

简介: 很多事情并不难,只是缺乏多走半里路的习惯!

1. 多走半里路


很多事情并不难,只是缺乏多走半里路的习惯!


反例

public boolean isInValid(String str) {
    if (str == null || str.trim().length() == 0) {
        return true;
    }
    return false;
}
复制代码


多走一步,海阔天空

public boolean isInValid(String str) {
  return (str == null) || (str.trim().length() == 0);
}
复制代码

是个程序员都知道哪个更好!


还有一种见过很多次的代码:

public boolean isEmptyName() {
    return StringUtils.isEmpty(name)? true: false;
}
复制代码


难道不感觉到多余吗?

再走半步,山清水秀

public boolean isEmptyName() {
    return StringUtils.isEmpty(name);
}
复制代码


2. 空对象


NullPointException,让我们防不胜防;尤其是使用第三方API,如果抛出这个异常,会让人有骂街的冲动。所以:若不想踩坑,请先别给别人挖坑。

理想要求:我们生产的所有public带返回值的方法体中,不应该出现“return null”字样。空对象就是这一思想的落地实现。



案例:查询并返回一个名字为steven的老师或者学生。

微信图片_20220616113510.jpg

public static SearchFilter name(String name) {
    return human -> human.getName().equalsIgnoreCase(name);
}
public Human find(SearchFilter filter) {
    for (Human human : humans) {
        if (filter.isMatched(human)) {
            return human;
        }
    }
    return new NullHuman();   //如果你返回NULL,总有一天你会挨骂!
}
//Test
assertFalse(edu.find(name("steven")).isNull());
复制代码


3. 单实例情结


谈起设计模式,很多人会觉得高大上,于是当学会了其中一个时,便会不加节制的使用。

单例,GOF中最简单的一个设计模式,很好用、也非常方便,但说实话很多人在滥用。我们的系统中随处可见,但用前请叩问自己该类真的是在系统当中有并且只能有一个实例吗?


否则请拒绝单实例。太多的单实例会给你的自动化测试带来无穷的烦恼。


4. 充分使用枚举


枚举有一些比较强大的特性容易被忽视,比如枚举对象本身也可以携带变量;这一特性让枚举在更多的场合发挥不可替代的功用。


举例:不同武器具有不同的杀伤力,杀伤力变量可以携带到武器的枚举值中。

public enum EquipmentEnum {
    Staff(20), Hammer(10);
    double playerRisedAbility;
    EquipmentEnum(double playerRiseAbility) {
        this.playerRisedAbility = playerRiseAbility;
    }
    public double getRaiseAbility(double originalAbility) {
        return Double.sum(originalAbility, playerRisedAbility);
    }
}
复制代码


5. 提高注释质量


30%的代码注释比的时代已经成为过去,最好的注释的就是无需注释,程序员应该致力于通过命名让注释消失。


大量的实践证明,随着重构频率越来越高,重构过程中随着代码的变更同步更新注释的可能性几乎为零。久而久之,注释与代码就是南辕北辙。


6. 面向客户的命名


好的命名能够愉悦读者的心情,让人有想一睹作者真面目的冲动!


案例:

一个房地产开发商一个新的楼盘开盘,原价80万每套,有如下优惠策略,但优惠政策可能随着楼盘存量情况会不一样,为开发商写一个房款计算程序

  1. 老客户买二套房可优惠4%,可累加
  2. 一次性全款客户可优惠3%,可累加

image.png

public class Customer {
    Benefiter benefit = new NullBenefiter();
    public void is(Benefiter benefits) { //原本的set方法,重命名会带来意想不到的效果。
        this.benefit = benefits;
    }
    public double shouldPay(double srcPrice) {
        return srcPrice - benefit.benefit(srcPrice);
    }
}
@FunctionalInterface
public interface Benefiter {
    abstract double benefit(double srcPrice);
    default Benefiter and(final Benefiter otherPolicy) {
        return srcPrice -> (benefit(srcPrice) + otherPolicy.benefit(srcPrice));
    }
}
复制代码


受益于良好命名,客户代码语义跃然纸上:customer.is(oldCustomer().and(fullPay()));


7. 使用多维数组


数组是任何一门相对高级的机器语言的必备数据结构,但是对面向对象开发人员而言,似乎对它有所忽略。


在一些特定的场景下,它能起到意想不到的效果。


案例:美元、法郎、人民币三币种间换算。其中美元:法郎=1:2;美元:人民币=1:8;

public class ExchangeRate {
    public static final int DOLLAR = 0;
    public static final int FRANC = 1;
    public static final int YMB = 2;
    private static final double[][] rates = {   //二维数组定义币种之间的汇率,简单高效
            {1.0d, 2.0d, 8.0d},
            {0.5d, 1.0d, 4.0d},
            {0.125d, 0.25d, 1.0d}
    };
    public static double getRateOf(Currency src, Currency tgt) {
        return rates[src.ID][tgt.ID];
    }
}
public class Cash {
    private double value;
    private Currency unit;
    public Cash(double value, Currency unit) {
        this.value = value;
        this.unit = unit;
    }
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Cash) {
            Cash other = (Cash) obj;
            return value == other.value * getRateOf(other.unit, unit);
        }
        return false;
    }
}
复制代码

如果汇率的二维数组使用配置文件定义并初始化,那还可以将设计提升一个新的高度——易于扩展,甚至不需修改任何Java代码即可完成币种的扩展。


8. 规约函数入参


当我们调用外部API时,最理想的就是零入参,即便有入参也希望是类型明确且范围可枚举的,这样会让我们使用这个API时更具安全感!心同此理,当你把一个函数声明为public时,请谨慎地定义它的入参。


如果把上述案例中获取两种币种之间的汇率函数:

public static double getRateOf(Currency src, Currency tgt) {
  return rates[src.ID][tgt.ID];
}
复制代码

定义为

public static double getRateOf(int src, int tgt) {
  return rates[src][tgt];
}
复制代码


虽然函数实现简单了一些,但可想而知,这个API会让使用者很忌惮!数组越界异常肯定是经常发生。

相反通过枚举规约了入参的类型和范围,给用户一些固定选项,使用者少了很多顾虑,而我们的程序也更加安全。


所以:我们对外提供的API入参类型尽量少用String、Int这种基本类型,给使用者发挥的空间越大,犯错的可能越大,因为你把风险暴露在不受控的用户手里。


9. 经常进行封装去重


你是否为反复地编写类似“if (tasks != null && tasks.size() > 0)”或者“if (name != null && (!name.isEmpty()))”的代码而心浮气躁,其实你完全可以解救自己。


封装一下

public class CollectionUtil {
    public static boolean isEmpty(Collection collection) {
        return collection == null || collection.isEmpty();
    }
}
复制代码

以及

public class StringUtil {
    public static boolean isEmpty(String context) {
        return context == null || context.isEmpty();
    }
}
复制代码


一切变得非常简单,心情好了,效率也就高了!


10. 化解if/else的毒素


我们的系统充斥着if/else的逻辑,因为它逻辑“简单”、易用!但随着时间推移、需求的扩展,有一天你会发现自己都看不懂自己写的if/else。


所以一开始就要学会拒绝这种逻辑的嵌套,又或者是当你看嵌套的复杂逻辑,大胆的对它进行重构。


案例:输入一个1-1000的数值N,如果N是3或者3的倍数时,返回“FIZZ”;如果N是5或者5的倍数时, 返回“BUZZ”;既是3的倍数又是5的倍数时, 返回“FIZZBUZZ”;其他的则输出N。


原实现:披着对象外壳的面向过程代码


public String say(int input) throws Exception {
    if ( input < 1 || input > 1000){
        throw new Exception("Invalid input");
    }
    if (input % 15 == 0) {
        return "FIZZBUZZ";
    }
    if (input % 5 == 0) {
        return "BUZZ";
    }
    if (input % 3 == 0) {
        return "FIZZ";
    }
    return String.valueOf(input);
}
复制代码

嗅一嗅其中的坏味道,如果将来需求扩展,比如引进7的倍数。照此逻辑,继续添加if,问题是解决了,但总有一天你会怀疑人生!


改进一:引入责任链和模板方法模式


image.png

public String say(int input) throws Exception {
    if (input < 1 || input > 1000) {
        throw new Exception("Invalid input");
    }
    DefaultParser defaultParser = new DefaultParser(null);
    MultiOfThreeParser multiOfThreeParser = new MultiOfThreeParser(defaultParser);
    MultiOfFiveParser multiFiveParser = new MultiOfFiveParser(multiOfThreeParser);
    MultiOfFifteenParser fifteenPaser = new MultiOfFifteenParser(multiFiveParser);
    return fifteenPaser.parse(input);
}
public abstract class Parser {
    abstract boolean isFixedResponsibility(int inputContext);
    abstract String response(int inputContext) throws Exception;
    public String parse(int inputContext) throws Exception  {
        if (isFixedResponsibility(inputContext))     {
            return response(inputContext);
        }
        if (nextParser != null) {
            return nextParser.parse(inputContext);
        }
        return NULL;
    }
}
public class MultiOfFifteenParser extends Parser {
    public MultiOfFifteenParser(Parser nextParser) {
        super(nextParser);
    }
    @Override
    public String response(int inputContext) throws Exception {
        return FLAG_FOR_MULIT_OF_FIFTEEN;
    }
    @Override
    boolean isFixedResponsibility(int inputContext)    {
        return inputContext % 15 == 0;
    }    
}
复制代码

看起来好多了,但感觉好像还有哪儿不太对劲!


改进二:彻底穿上对象的外袍


敏锐的读者可能还会发现上述改进还存在细微的小问题,那就是say方法里面的异常分支处理。怎么还存在一个if,对于一个完美主义强迫症患者而言,它的存在就是一个灾难。继续改进一下:

image.png

public String say(int input) throws Exception {
    DefaultParser defaultParser = new DefaultParser(null);
    MultiOfThreeParser multiOfThreeParser = new MultiOfThreeParser(defaultParser);
    MultiOfFiveParser multiOfFiveParser = new MultiOfFiveParser(multiOfThreeParser);
    MultiOfFifteenParser FifteenPaser = new MultiOfFifteenParser(multiOfFiveParser);
    InvalidNumberParser invalidNumberPaser = new InvalidNumberParser(FifteenPaser);
    return invalidNumberPaser.parse(input);
}
public class InvalidNumberParser extends Parser {
    public InvalidNumberParser(Parser nextParser) {
        super(nextParser);
    }
    @Override
    String response(int inputContext) throws Exception {
        throw new Exception("Invalid input");
    }
    @Override
    boolean isFixedResponsibility(int inputContext){
        return (inputContext < L_THRESHOLD) || (inputContext > H_THRESHOLD);
    }
}
复制代码

OK,Perfect!



目录
相关文章
|
SQL Oracle 关系型数据库
Navicat Premium 15激活方式
介绍如何安装使用Navicat Premium 15
|
存储 前端开发 PHP
构建一个简单的网站,包括用户注册、登录功能
构建一个简单的网站,包括用户注册、登录功能
1034 1
|
SQL Java 测试技术
再也不需要手写 SQL 造数据了
DBeaver 是一个功能非常完善的数据库客户端,它有 开源免费版本:https://github.com/dbeaver/dbeaver, 企业版:https://dbeaver.com/
再也不需要手写 SQL 造数据了
|
7月前
|
数据采集 JSON API
Python 实战:用 API 接口批量抓取小红书笔记评论,解锁数据采集新姿势
小红书作为社交电商的重要平台,其笔记评论蕴含丰富市场洞察与用户反馈。本文介绍的小红书笔记评论API,可获取指定笔记的评论详情(如内容、点赞数等),支持分页与身份认证。开发者可通过HTTP请求提取数据,以JSON格式返回。附Python调用示例代码,帮助快速上手分析用户互动数据,优化品牌策略与用户体验。
1340 3
|
7月前
|
JSON 监控 API
1688 商品列表 API 深度拆解:从参数配置到数据获取
1688 是重要的批发采购平台,其商品列表 API 接口为开发者、商家和数据分析人员提供批量获取商品基础信息(如名称、价格、销量等)的能力。该接口支持市场调研、竞品分析等场景,助力商业决策与效率提升。接口基于 HTTPS 协议,采用 GET 或 POST 请求方式,需提供通用参数(如 app_key、timestamp 等)和业务参数(如 category_id、page_no 等)。响应数据以 JSON 格式返回,包含商品详情及分页信息。
253 13
|
7月前
|
Ubuntu 数据库 虚拟化
Windows 环境下 Odoo 安装保姆级教程
本教程详细介绍了在 Windows 系统上通过虚拟机部署 Odoo 的完整流程。首先确认硬件需求,确保 CPU、内存和磁盘空间满足最低配置;接着安装 VMware Workstation Pro 并创建 Ubuntu 虚拟机,配置桥接网络以实现主机与虚拟机的通信;随后借助微聚云快速安装预配置好的 Odoo 环境,简化复杂环境搭建;最后通过浏览器访问虚拟机 IP,完成 Odoo 数据库初始化及基础设置。整个过程清晰易懂,适合新手快速上手 Odoo 部署。
893 4
|
存储 Java 编译器
🔍深入Android底层,揭秘JVM与ART的奥秘,性能优化新视角!🔬
【7月更文挑战第28天】在Android开发中,掌握底层机制至关重要。从Dalvik到ART, Android通过采用AOT编译在应用安装时预编译字节码至机器码,显著提升了执行效率。ART还优化了垃圾回收,减少内存占用及停顿。为了优化性能,可减少DEX文件数量、优化代码结构利用内联等技术、合理管理内存避免泄漏,并使用ART提供的调试工具。
318 7
|
9月前
|
边缘计算 文字识别 自然语言处理
当OCR遇见大语言模型:智能文本处理的进化之路
简介:本文探讨光学字符识别(OCR)技术与大语言模型(LLM)结合带来的革新。传统OCR在处理模糊文本、复杂排版时存在局限,而LLM的语义理解、结构解析和多模态处理能力恰好弥补这些不足。文中通过代码实例展示了两者融合在错误校正、文档解析、多语言处理、语义检索及流程革新上的五大优势,并以财务报表解析为例,说明了该技术组合在实际应用中的高效性。此外,文章也展望了未来的技术发展趋势,包括多模态架构、小样本学习和边缘计算部署等方向,预示着文本处理技术正迈向智能认知的新时代。(240字)
|
安全 Oracle 关系型数据库
智能合约中时间依赖漏洞
智能合约中时间依赖漏洞
366 6
|
SQL 监控 数据挖掘
实时计算Flink版体验评测
一文带你弄懂实时计算Flink版场景实践和核心功能体验
835 16