百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

「解锁新姿势」 兄dei,你代码需要优化了

ztj100 2025-06-04 08:59 34 浏览 0 评论

前言

在我们平常开发过程中,由于项目时间紧张,代码可以用就好,往往会忽视代码的质量问题。甚至有些复制粘贴过来,不加以整理规范。往往导致项目后期难以维护,更别说后续接手项目的人。所以啊,我们要编写出优雅的代码,方便你我他,岂不美哉?

下面分享一些我在开发中常用的编码中小建议,如有不妥,欢迎大家一起交流学习。

卫语句

卫语句,就是把复杂的条件表达式拆分成多个条件表达式。比如 多个 if-elseif-else 嵌套, 可以拆分成多个 if。如下面代码

代码:


-------------------- before  --------------------

public void today() {
    if (isWeekend()) {
        if (isFee()) {
            System.out.println("study Android");
        } else {
            System.out.println("play a game");
        }
    } else {
        System.out.println("go to work");
    }
}


 -------------------- after  (建议) --------------------

public void today() {

    // 提前过滤掉`特殊情况`
    if (!isWeekend()) {
        System.out.println("go to work");
        return; // 提前return
    }

    //提前过滤掉`特殊情况`
    if (isFee()) {
        System.out.println("study Android");
        return; // 提前return
    }

    // 更关注于 `核心业务`代码实现。
    System.out.println("play a game");
}

小函数

我们平常开发的时候,应该编写小而美函数,避免函数过长。一般函数最好在15行以内(建议) 我们看看下面代码:


-------------------- before  --------------------

if (age > 0 && age < 18){
    System.out.println("小孩子");
}

if (number.length() == 11){
    System.out.println("符合手机号");
}

-------------------- after (建议) --------------------

private static boolean isChild(int age) {
    return age > 0 && age < 18;
}

private static boolean isPhoneNumber(String number) {
    return number.length() == 11;
}

if (isChild(age)){
    System.out.println("小孩子");
}

if (isPhoneNumber(number)){
    System.out.println("符合手机号");
}

把判断语句抽取成一个个小函数, 这样代码更加清晰明了。

迪米特法则

概念:

迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解。例如 当一条语句中 一个对象出现两个 . (student.getName().equals("张三")) 就是代码坏味道的表现,如下代码所示。

代码:


-------------------- before  --------------------

public class Student {

    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public static void main(String[] args) {

    Student student = new Student("张三");

    // 注意看这里,
    // 这里获取 student的name属性,在根据name属性进行判断
    if (StringUtils.isNotBlank(student.getName()) && student.getName().equals("张三")) {
        System.out.println("我的好朋友是 " + student.getName());
    }
}


 -------------------- after (建议) --------------------
 
 public class Student {

    ... 省略name代码

    // 新增一个 判断是否是我的好朋友方法
    public boolean isGoodFriend(){
        return StringUtils.isNotBlank(this.name) && this.name.equals("张三");
    }
}

public static void main(String[] args) {

    Student student = new Student("张三");

    // 根据迪米特法则,把判断逻辑,抽取到 Student 内部,暴露出方法(isGoodFriend)
    if (student.isGoodFriend()){
        System.out.println("我的好朋友是 " + student.getName());
    }
}

IDEA/Android Studio 抽取方法快捷键: option + command + M

Map 提取对象

我们在平常开发中,会使用到map,但是在面向对象开发理念中,一个 map的使用,往往就会错过了 Java Bean。建议使用 Java Bean 更直观。如下代码:

public static void main(String[] args) {

    -------------------- before  --------------------
        Map<String, String> studentMap = new HashMap<>();
        studentMap.put("张三", "男");
        studentMap.put("小红", "女");
        studentMap.put("李四", "男");

        studentMap.forEach((name, sex) -> {
            System.out.println(name + " : " + sex);
        });

    -------------------- after (建议)  --------------------
    
        List<Student> students = new ArrayList<>();
        students.add(new Student("张三", "男"));
        students.add(new Student("小红", "女"));
        students.add(new Student("李四", "男"));

        for (Student student : students) {
            System.out.println(student.getName() + ":" + student.getSex());
        }
    }

笔者在编写这点时候,有所顾虑。肯定有小伙伴跳出来说,map 和 bean 不是一样吗?用map 我还可以省去思考如何命名Class呢。但是从代码规范来说,这样代码设计不是更符合 Java 面向对象的思想吗?

Stream

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。使得代码调用起来更加优雅~ 直接来看代码:

public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("张三", "男"));
        students.add(new Student("李四", "男"));
        students.add(new Student("小红", "女"));
        students.add(new Student("小花", "女"));
        students.add(new Student("小红", "女"));
        
        -------------------- before  --------------------
        //统计男生个数
        //传统的 for each 循环遍历
        long boyCount = 0;
        for (Student student : students) {
            if (student.isBoy()) {
                boyCount++;
            }
        }

        System.out.println("男生个数 = " + boyCount);

        -------------------- after (建议)  --------------------

        //统计男生个数
        //stream 流遍历
        long count = students.stream()
                .filter(Student::isBoy) // 等同于.filter(student -> student.isBoy())
                .count();
                
        System.out.println("男生个数 = " + boyCount);
}

相比与 传统的 For 循环,更推荐大家使用 stream 遍历。 stream 流的链式调用,还有许多骚操作,如 sorted, map, collect等操作符,可以省去不必要if-else,count等判断逻辑。

多态

Java 三大特性之一,多态,相信大家都不会陌生,多态的好处就是根据对象不同类型采取不同的的行为。我们常常在编写 switch 语句的时候,如果改用多态,可以把每个分支,抽取到一个子类内的覆写函数中,这就更加灵活。

我们有这样一个需求,编写一个简单计算器方法,我们先来看一小段代码:



    -------------------- before  --------------------
    public static int getResult(int numberA, int numberB, String operate) {
        int result = 0;
        switch (operate) {
            case "+":
                result = numberA + numberB;
                break;
            case "-":
                result = numberA - numberB;
                break;
            case "*":
                result = numberA * numberB;
                break;
            case "/":
                result = numberA / numberB;
                break;
        }
        return result;
    }
    
    -------------------- after (建议)  --------------------
    
    abstract class Operate {
        abstract int compute(int numberA, int numberB);
    }
    
    class AddOperate extends Operate {

        @Override
        int compute(int numberA, int numberB) {
            // TODO 在这里处理相关逻辑
            return numberA + numberB;
        }
    }
    
    ... SubOperate, MulOperate, DivOperate 也和 AddOperate一样这里就不一一贴出
    
    public static int getResult(int numberA, int numberB, String operate) {
        int result = 0;
        switch (operate) {
            case "+":
                result = new AddOperate().compute(numberA, numberB);
                break;
            case "-":
                result = new SubOperate().compute(numberA, numberB);
                break;
            case "*":
                result = new MulOperate().compute(numberA, numberB);
                break;
            case "/":
                result = new DivOperate().compute(numberA, numberB);
                break;
        }
        return result;
    }

有小伙伴可能会说,你这不是更复杂了吗?

对比起单纯的switch,我们可以这样理解:

  • 虽然在类上有所增加,但是通过多态,把对应操作的逻辑分离出来,使得代码耦合度降低。
  • 如果要修改对应加法的逻辑, 我们只需要修改对应 AddOperate类就可以了。避免直接修改getResult 方法
  • 代码可读性更好,语义更加明确。
  • 但是这里会存在一些问题,如果我们新增一个平方根,平方等计算方式, 就需要修改 switch 里面的逻辑,新增一个条件分支。下面我们再来看看更进一步的优化。

    反射

    通过上面例子,我们可以进一步优化,通过反射生成对应的 Class,然后在调用compute方法。如下代码:

    public static <T extends Operate> int getResult(int numberA, int numberB, Class<T> clz) {
            int result = 0;
            try {
                return clz.newInstance().compute(numberA, numberB);
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
                return result;
            }
    }
    
    
    public static void main(String[] args) {
        // 调用的时候直接传递 class 即可
        System.out.println(getResult(1, 2, SumOpearte.class));
    }

    根据传入 class 参数,然后生成对应 Opearte处理类, 对比多态方式,我们这里采用反射,使得代码耦合度大大降低,如果在增加平方根,平方等计算方式。我们只需要 新增一个 class 继承 Opearte 即可,getResult 不用做任何修改。

    需要注意的是,不是所有switch语句都需要这样替换, 在面对简单的 switch语句,就不必要了, 避免过度设计的嫌疑。如下代码:

    
    public String getResult(int typeCode) {
            String type = "";
            switch (typeCode) {
                case 0:
                    type = "加法";
                    break;
                case 1:
                    type = "减法";
                    break;
                case 2:
                    type = "乘法";
                    break;
                case 3:
                    type = "除法";
                    break;
            }
            return type;
    }

    最后

    以上就是我在编码上的一些小建议,如有不妥,欢迎大家一起交流学习。

    零基础学习Java编程,可以加个关注,需要Java基础学习资料总结,可以私信我,发送“编程”可以收到入口。

    原链接:
    https://juejin.cn/post/6844903983744548877

    相关推荐

    Linux集群自动化监控系统Zabbix集群搭建到实战

    自动化监控系统...

    systemd是什么如何使用_systemd/system

    systemd是什么如何使用简介Systemd是一个在现代Linux发行版中广泛使用的系统和服务管理器。它负责启动系统并管理系统中运行的服务和进程。使用管理服务systemd可以用来启动、停止、...

    Linux服务器日常巡检脚本分享_linux服务器监控脚本

    Linux系统日常巡检脚本,巡检内容包含了,磁盘,...

    7,MySQL管理员用户管理_mysql 管理员用户

    一、首次设置密码1.初始化时设置(推荐)mysqld--initialize--user=mysql--datadir=/data/3306/data--basedir=/usr/local...

    Python数据库编程教程:第 1 章 数据库基础与 Python 连接入门

    1.1数据库的核心概念在开始Python数据库编程之前,我们需要先理解几个核心概念。数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它就像一个电子化的文件柜,能让我们高效...

    Linux自定义开机自启动服务脚本_linux添加开机自启动脚本

    设置WGCloud开机自动启动服务init.d目录下新建脚本在/etc/rc.d/init.d新建启动脚本wgcloudstart.sh,内容如下...

    linux系统启动流程和服务管理,带你进去系统的世界

    Linux启动流程Rhel6启动过程:开机自检bios-->MBR引导-->GRUB菜单-->加载内核-->init进程初始化Rhel7启动过程:开机自检BIOS-->M...

    CentOS7系统如何修改主机名_centos更改主机名称

    请关注本头条号,每天坚持更新原创干货技术文章。如需学习视频,请在微信搜索公众号“智传网优”直接开始自助视频学习1.前言本文将讲解CentOS7系统如何修改主机名。...

    前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令

    在Linux服务器管理中,SSH(SecureShell)是远程操作的核心工具。以下是SSH终端操作的常用命令和技巧,涵盖连接、文件操作、系统管理等场景:一、SSH连接服务器1.基本连接...

    Linux开机自启服务完全指南:3步搞定系统服务管理器配置

    为什么需要配置开机自启?想象一下:电商服务器重启后,MySQL和Nginx没自动启动,整个网站瘫痪!这就是为什么开机自启是Linux运维的必备技能。自启服务能确保核心程序在系统启动时自动运行,避免人工...

    Kubernetes 高可用(HA)集群部署指南

    Kubernetes高可用(HA)集群部署指南本指南涵盖从概念理解、架构选择,到kubeadm高可用部署、生产优化、监控备份和运维的全流程,适用于希望搭建稳定、生产级Kubernetes集群...

    Linux项目开发,你必须了解Systemd服务!

    1.Systemd简介...

    Linux系统systemd服务管理工具使用技巧

    简介:在Linux系统里,systemd就像是所有进程的“源头”,它可是系统中PID值为1的进程哟。systemd其实是一堆工具的组合,它的作用可不止是启动操作系统这么简单,像后台服务...

    Red Hat Enterprise Linux 10 安装 Kubernetes (K8s) 集群及高级管理

    一、前言...

    Linux下NetworkManager和network的和平共处

    简介我们在使用CentoOS系统时偶尔会遇到配置都正确但network启动不了的问题,这问题经常是由NetworkManager引起的,关闭NetworkManage并取消开机启动network就能正...

    取消回复欢迎 发表评论: