美文网首页
让java代码变的更优雅

让java代码变的更优雅

作者: 紫厢雨 | 来源:发表于2020-01-11 15:14 被阅读0次

    前言

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

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

    卫语句

    卫语句,就是把复杂的条件表达式拆分成多个条件表达式。比如 多个 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

    来源:掘金

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

    相关文章

      网友评论

          本文标题:让java代码变的更优雅

          本文链接:https://www.haomeiwen.com/subject/xazvactx.html