[解锁新姿式] 兄dei,你代码须要优化了

黑客(程序员)也是创做者,与画家、建筑师、做家同样。 ——《黑客与画家》java

前言

在咱们日常开发过程当中,因为项目时间紧张,代码能够用就好,每每会忽视代码的质量问题。甚至有些复制粘贴过来,不加以整理规范。每每致使项目后期难以维护,更别说后续接手项目的人。因此啊,咱们要编写出优雅的代码,方便你我他,岂不美哉?程序员

下面分享一些我在开发中经常使用的编码中小建议,若有不妥,欢迎你们一块儿交流学习。ide

卫语句

卫语句,就是把复杂的条件表达式拆分红多个条件表达式。好比 多个 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("符合手机号");
}
复制代码

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

迪米特法则

概念:

迪米特法则(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 (student.getName().equals("张三")) {
        System.out.println("个人好朋友是 " + student.getName());
    }
}


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

    ... 省略name代码

    // 新增一个 判断是不是个人好朋友方法
    public boolean isGoodFriend(){
        return 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 + Mspa

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());
        }
    }
复制代码

笔者在编写这点时候,有所顾虑。确定有小伙伴跳出来讲,mapbean 不是同样吗?用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-elsecount等判断逻辑。

多态

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;
}
复制代码

最后

以上就是我在编码上的一些小建议,若有不妥,欢迎你们一块儿交流学习。

相关文章
相关标签/搜索