让java代码变的更优雅

前言


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

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

卫语句

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

好处:提前过滤掉特殊情况,更关注核心业务逻辑

、、、java

-------------------- 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());

    }

}


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 我还可以省去思考如何命名Class呢。但是从代码规范来说,这样代码设计不是更符合 Java 面向对象的思想

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里面的逻辑,新增一个条件分支。下面我们再来看看更进一步的优化。

反射

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;

}


作者:hyzhan43

链接:https://juejin.im/post/5dafbc02e51d4524a0060bdd

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容