优化if else

北战南征 提交于 2020-01-04 17:25:40

优化 if else

参考:《阿里巴巴java编码规范》、《设计模式》(清华大学出版)、https://blog.csdn.net/mifffy_java/article/details/95201289

1、 阿里编程归约

对于控制语句,阿里归约上推荐了一种思路。【推荐】在表达异常的分支时,尽量少用 if else 方式,这种方式可以改写成:

if(condition){
    ...
    return obj;
}

// 接着写 else 的业务逻辑代码

说明:如果不得不使用 if()...else if()...else... 方式表达逻辑,【强制】避免后序代码维护困难,请勿超过3层。

正例:超过3层的 if else 逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句示例如下:

public void today(){
    if(isBusy()){
        System.out.println("change time.");
        return;
    }
    
    if(isFree()){
        System.out.println("go to travel.");
        return;
    }
    
    Sysout.out.println("stay at home. learning java.");
    return;
}

说明:卫语句就是把复杂的条件表达式拆分成多个条件表达式,条件为真时,立刻从方法体中返回给调用方。卫语句的好处是条件表达式之间相互独立,不会互扰。

2、 switch 语句

多个if else的写法:

char status = 'B';
if('A' == status){
    functionA();
} else if('B' == status){
    functionB();
} else if('C' == status){
    functionC();
} else {
    ...
}

使用switch case 语句:

switch(status){
    case 'A':
        functionA();
        break;
    case 'B':
        functionB();
        break;
    case 'C':
        functionC();
        break;
    default:
        break;
}

3、数组

来自google解释,这是一种编程模式,叫做表驱动法,本质是从表里查询信息来代替逻辑语句。

【案例1】

定义一个接口数组,这是一个函数式接口,其中的方法是单参数无返回值的。

可以使用不同的样式打印传入的参数:

Consumer[] consumers = {
    System.out::println,
    arg -> System.out.println("[" + arg + "]"),
    arg -> System.out.println("(" + arg + ")"),
    arg -> System.out.println("{" + arg + "}")
};

for (Consumer consumer : consumers) {
    consumer.accept("这句话会打印出来");
}

【案例2】

if else 的写法:

定义一个方法,获取星期数。

private static String getWeekDay(int weekStat) {
    if (0 == weekStat) {
        return "星期天";
    }
    if (1 == weekStat) {
        return "星期一";
    }
    if (2 == weekStat){
        return "星期二";
    }
    if (3 == weekStat){
        return "星期三";
    }
    if (4 == weekStat){
        return "星期四";
    }
    if (5 == weekStat){
        return "星期五";
    }
    if (6 == weekStat){
        return "星期六";
    }
    return null;
}

// 使用该方法获取星期一
int weekStat = 1;
String weekDay = getWeekDay(1);
System.out.println(weekDay);

使用数组优化:

String[] week = {
    "星期天",
    "星期一",
    "星期二",
    "星期三",
    "星期四",
    "星期五",
    "星期六"
};

// 使用数组获取星期一
int weekStat = 1;
String weekDay = week[weekStat];
System.out.println(weekDay)

4、Optional解决判空的if else

环境:基于 jdk8 (高版本还有更厉害、方便的方法,这里不做讨论)

【案例1】

// 图片名
String pictureName = "真香.png";
// 当其不为空时,打印在控制台
Optional.of(pictureName).ifPresent(System.out::println);

【案例2】

/**
 * 举个例子:实体类;篇幅原因就不写get set方法了。
 */
class User {
    String name;
    String password;
}
public class Demo {
    public static void main(String[] args) {
        User user = new User();
        user.name = "小冯";

        // 过滤到密码是 123456 的目标用户
        // 若用户不为 null 则打印用户信息
        Optional.of(user)
                .filter(u -> "123456".equals(u.password))
                .ifPresent(System.out::println);
        
        // 当用户名存在时,打印用户名
        Optional.of(user).map(u -> u.name).ifPresent(System.out::println);
    }
}

5、策略设计模式

概念:来自《设计模式》(清华大学出版社)

策略设计模式用于算法的自由切换和扩展。策略模式对应于解决某一问题的一个算法族,允许用户从该算法中任选一个算法解决某一问题,同时可以方便的更换算法或增加新的算法。

5.1 枚举实现

定义一个枚举类:

/**
 * 定义了一周的每一天要干什么
 */
enum WeekDay {
    /**星期天要作甚?*/
    Sunday("Sunday"){
        @Override
        void weekRun() {
            System.out.println(weekName + ":和朋友出去玩!");
        }
    },
    /**星期1要作甚?*/
    Monday("Monday"){
        @Override
        void weekRun() {
            System.out.println(weekName + ":在学校学习英语");
        }
    },
    /**星期2要作甚?*/
    Tuesday("Tuesday"){
        @Override
        void weekRun() {
            System.out.println(weekName + ":在学校学习java");
        }
    },
    /**星期3要作甚?*/
    Wednesday("Wednesday"){
        @Override
        void weekRun() {
            System.out.println(weekName + ":在学校学习语文");
        }
    },
    /**星期4要作甚?*/
    Thursday("Thursday"){
        @Override
        void weekRun() {
            System.out.println(weekName + ":在学校学习历史");
        }
    },
    /**星期5要作甚?*/
    Friday("Friday"){
        @Override
        void weekRun() {
            System.out.println(weekName + "在学校玩");
        }
    },
    /**星期6要作甚?*/
    Saturday("Saturday"){
        @Override
        void weekRun() {
            System.out.println(weekName + "和朋友在家看电影");
        }
    };

    public String weekName;
    abstract void weekRun();
    WeekDay(String weekName){
        this.weekName = weekName;
    }
}

使用该枚举类:

public class Demo {
    public static void main(String[] args) {
        // 调用的写法1
        WeekDay.valueOf("Sunday").weekRun();
        // 调用的写法2
        WeekDay.Monday.weekRun();
    }
}

5.2 多态实现

【案例1】

此处参见我的数组的那个案例,使用函数式接口的数组,实现了多态。

【案例2】

当不使用 Lambda 优化写法时,我重新设计了一个场景,抽象一个跑的行为抽象:

/**
 * 跑的行为抽象
 */
@FunctionalInterface
interface RunStrategy {
    void run();
}

针对该行为抽象,有几种实现:

/**
 * 人跑的行为
 */
class PeopleRunStrategy implements RunStrategy {
    @Override
    public void run() {
        System.out.println("人用腿跑");
    }
}

/**
 * 汽车跑的行为
 */
class CarRunStrategy implements RunStrategy {

    @Override
    public void run() {
        System.out.println("汽车用轮子跑");
    }
}

/**
 * 鱼跑的行为
 */
class FishRunStrategy implements RunStrategy {

    @Override
    public void run() {
        System.out.println("鱼在水里游");
    }
}

调用:

RunStrategy peopleRun = new PeopleRunStrategy();
peopleRun.run();

5.3 map 优化

针对 5.2 多态实现的策略设计模式,使用映射来调用:

Map<String, RunStrategy> runStrategyMap = new HashMap<>(16);
runStrategyMap.put("people", new PeopleRunStrategy());
runStrategyMap.put("car", new CarRunStrategy());
runStrategyMap.put("fish", new FishRunStrategy());

// 获取汽车的跑的实现,当没有该实现时,调用人的跑的实现
RunStrategy runStrategy1 = runStrategyMap.getOrDefault("car", new PeopleRunStrategy());
// 获取鸟的跑的实现,当没有该实现时,调用人的跑的实现
RunStrategy runStrategy2 = runStrategyMap.getOrDefault("bird", new PeopleRunStrategy());
// 打印:汽车用轮子跑
runStrategy1.run();
// 打印:人用腿跑
runStrategy2.run();

5.4 工厂设计模式优化

接口与实现类:

与 5.2 中的【案例2】一致。

工厂类:

/**
 * 工厂类
 */
class RunStrategyFactory {
    private RunStrategyFactory(){}

    private static final Map<String, RunStrategy> RUN_STRATEGY_MAP = new ConcurrentHashMap<>(16);
    /*
     * 初始化数据:在Spring框架中,使用反射扫描了包、类,
     * 再通过类名反射出目标对象,这里使用静态代码块代替这一步骤,
     * 直接将对象放入 map 容器中。
     */
    static {
        RUN_STRATEGY_MAP.put("peopleRunStrategy", new PeopleRunStrategy());
        RUN_STRATEGY_MAP.put("fishRunStrategy", new FishRunStrategy());
    }

    /**
     * 从容器中获取实例:默认为人的策略对象
     * @param strategy 策略对象、行为
     * @return {@link RunStrategy}
     */
    public static RunStrategy getInstance(String strategy){
        return RUN_STRATEGY_MAP.getOrDefault(strategy, new PeopleRunStrategy());
    }

    public static Map<String, RunStrategy> getRunStrategyMap(){
        return RUN_STRATEGY_MAP;
    }

    /**
     * 给容器中注册一个策略实例
     * @param runStrategy RunStrategy实现类的对象:不能用Lambda;不能用匿名内部类
     */
    public static void registerStrategy(RunStrategy runStrategy){
        String strategy = runStrategy.getClass().getSimpleName();
        // 将首字母小写并返回结果
        strategy = strategy.substring(0, 1).toLowerCase() + strategy.substring(1);
        RUN_STRATEGY_MAP.put(strategy, runStrategy);
    }

    /**
     * 给容器中注册一个策略实例
     * @param runStrategy RunStrategy实现类的对象或行为
     */
    public static void registerStrategy(String strategy, RunStrategy runStrategy){
        RUN_STRATEGY_MAP.put(strategy, runStrategy);
    }
}

运行测试:

public static void main(String[] args) {
    // 直接获取
    RunStrategyFactory.getInstance("fishRunStrategy").run();

    // 注册实现类
    RunStrategyFactory.registerStrategy(new CarRunStrategy());
    // 注册行为
    RunStrategyFactory.registerStrategy("snakeRunStrategy",() -> System.out.println("蛇在地上爬"));

    // 遍历执行
    RunStrategyFactory.getRunStrategyMap().forEach((k, v) -> {
        System.out.println("Strategy = " + k);
        v.run();
    });
}

运行结果:

鱼在水里游
Strategy = carRunStrategy
汽车用轮子跑
Strategy = peopleRunStrategy
人用腿跑
Strategy = fishRunStrategy
鱼在水里游
Strategy = snakeRunStrategy
蛇在地上爬

6、 状态设计模式

概念:来自《设计模式》(清华大学出版社)

状态设计模式是一种较为复杂的设计模式,它用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态设计模式。

该模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

状态模式描述了对象状态的变化以及对象如何在每一种状态下表现出不同的状态。

【案例】

抽象状态行为接口:

/**
 * Created by Feng on 2020/1/4 11:37<br>
 *     抽象状态行为:定义不同状态对应的方法
 *     方法的实现由其实现类完成
 */
@FunctionalInterface
interface State {
    void handle();
}

具体的状态类:

/**
 * 具体的状态类:空闲状态
 */
class FreeState implements State {

    @Override
    public void handle() {
        System.out.println("闲着就写代码!!");
    }
}

/**
 * 具体的状态类:繁忙状态
 */
class BusyState implements State {

    @Override
    public void handle() {
        System.out.println("忙了还得写代码!!!");
    }
}

环境类:

/**
 * 环境类:拥有状态的对象。
 * 在环境中维护一个抽象状态类 State 的实例,这个实例定义当前状态。
 * 还可以定义初始状态
 */
class Context {
    private State state;

    /**
     * 设置状态:调用handle方法
     */
    public void setState(State state){
        this.state = state;
        state.handle();
    }

    public State getState(){
        return state;
    }
}

客户端测试:

/**
 * 客户端:测试
 */
public class ContextClient {
    public static void main(String[] args) {
        Context context = new Context();
        context.setState(new FreeState());
        context.setState(new BusyState());

        context.setState(() -> System.out.println("这是Lambda 状态啊!!!"));
    }
}

测试结果:

闲着就写代码!!
忙了还得写代码!!!
这是Lambda 状态啊!!!

7、 职责链设计模式

概念:来自《设计模式》(清华大学出版社)

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

【案例】:模拟《LOL》游戏场景。假设,有个游戏角色叫【盖伦】,满血量有5200,纯防御装备。现在有对手阵营的角色【亚索】,【光辉】,【诺手】来抓他。盖伦有血量,当盖伦血量低于 2000时,【光辉】可以杀他;当盖伦血量低于4000时,【亚索】可以杀他;当盖伦血量低于5200时,【诺手】可以杀他。

能想到这个案例,我也是秀了(参考于英雄联盟游戏,哈哈)

友军抽象类:

/**
 * 友军抽象类:子类是亚索、光辉、诺手
 */
abstract class BaseFriendly {
   protected BaseFriendly friendly;

   public void setFriendly(BaseFriendly friendly){
        this.friendly = friendly;
   }

    /**
     * 友军攻击敌军
     * @param enemy 敌军
     */
   abstract void handleEnemy(BaseEnemy enemy);
}

友军子类:

/**
 * 亚索:可杀4000血以下的盖伦;否则交给诺手
 */
class YaSuo extends BaseFriendly {

    @Override
    void handleEnemy(BaseEnemy enemy) {
        int hp = 4000;
        if(enemy.getHp() <= hp){
            System.out.println(enemy.getName() + "的血量小于4000, 亚索杀了他。");
        } else {
            setFriendly(new NuoShou());
            System.out.println(enemy.getName() + "的血量是" + enemy.getHp() + ",亚索打不过,亚索叫来了诺手");
            this.friendly.handleEnemy(enemy);
        }
    }
}

/**
 * 光辉:可杀2000血以下的盖伦;否则交给亚索
 */
class GuangHui extends BaseFriendly {
    @Override
    void handleEnemy(BaseEnemy enemy) {
        int hp = 2000;
        if(enemy.getHp() <= hp){
            System.out.println(enemy.getName() + "的血量小于2000, 光辉杀了他。");
        } else {
            setFriendly(new YaSuo());
            System.out.println(enemy.getName() + "的血量是" + enemy.getHp() + ",光辉打不过,光辉叫来了亚索!");
            this.friendly.handleEnemy(enemy);
        }
    }
}

/**
 * 诺手:可杀满血盖伦
 */
class NuoShou extends BaseFriendly {

    @Override
    void handleEnemy(BaseEnemy enemy) {
        System.out.println(enemy.getName() + "的血量是" + enemy.getHp() + ",诺手杀了他。");
    }
}

敌军抽象类:

/**
 * 敌军抽象类
 */
abstract class BaseEnemy{
    /**
     * 敌军血量
     */
    private int hp;
    /**
     * 敌军名字
     */
    private String name;
    public BaseEnemy (String name, int hp){
        this.name = name;
        this.hp = hp;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHp() {
        return hp;
    }

    public void setHp(int hp) {
        this.hp = hp;
    }
}

敌军子类:

/**
 * 盖伦
 */
class GaiLun extends BaseEnemy {

    public GaiLun(String name, int hp) {
        super(name, hp);
    }
}

客户端测试:

public class HandlerClient {
    public static void main(String[] args) {
        // 创建盖伦
        BaseEnemy gaiLun = new GaiLun("盖伦", 4700);

        BaseFriendly guangHui = new GuangHui();
        guangHui.handleEnemy(gaiLun);

        System.out.println("------------------------");
        // 盖伦血量是1880时
        gaiLun.setHp(1880);
        guangHui.handleEnemy(gaiLun);
    }
}

测试结果:

盖伦的血量是4700,光辉打不过,光辉叫来了亚索!
盖伦的血量是4700,亚索打不过,亚索叫来了诺手
盖伦的血量是4700,诺手杀了他。
------------------------
盖伦的血量小于2000, 光辉杀了他。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!