改写if-else的几个思路

举报
技术火炬手 发表于 2021/02/22 17:37:10 2021/02/22
【摘要】 这种场景你有遇到过吗?

1. 卫语句提前return

假设有如下代码

	if (condition) {
	    // do something
	} else {
	    return xxx;
	}

通过对判断条件取反,代码在逻辑表达上会更加清晰

	if (!condition) {
	    return xxx;
	}
	// do something

2. 使用Optional简化if判空

2.1 简化1级判空

假设有如下代码

	if (input != null) {
	    // return value 1
	} else {
	    // return value 2
	}

使用Optional后

	return Optional.ofNullable(input).map(value1).orElse(value2);

2.2 简化多级判空

假设有如下代码

	if (input != null && input.getUser() != null && input.getUser().getName() != null) {
	    // do action 1
	} else {
	    // do action 2
	}

使用Optional后

	Optional.ofNullable(input)
	    .map(Input::getUser)
	    .map(User::getName)
	    .map(action1)
	    .orElse(action2);

对于没有else的场景,使用ifPresent即可

	if (input != null && input.getUser() != null && input.getUser.getName() != null) {
	    // do action
	}
	Optional.ofNullable(input)
	    .map(Input::getUser)
	    .map(User::getName)
	    .ifPresent(action);

3. 策略模式

假设有如下代码:

	if ("dog".equals(petType)) {
	    // 处理dog
	} else if ("cat".equals(petType)) {
	    // 处理cat
	} else if ("pig".equals(petType)) {
	    // 处理pig
	} else if ("rabbit".equals(petType)) {
	    // 处理rabbit
	} else {
	    throw new UnsupportedOperationException();
	}

这就是不要根据不同的参数类型走不同的代码逻辑,这种场景很常见,他还会以switch-case的方式出现:

	switch (petType) {
	    case "dog":
	        // 处理dog
	        break;
	    case "cat":
	        // 处理cat
	        break;
	    case "pig":
	        // 处理pig
	        break;
	    case "rabbit":
	        // 处理rabbit
	        break;
	    default:
	        throw new UnsupportedOperationException();
	}

不同的代码逻辑就代表了不同的策略,我们可以通过如下几个方式改写。

3.1 多态

	public interface Strategy {
	    void invoke(); // 处理各个逻辑
	}
	public class DogStrategy implements Strategy {
	    @Override
	    public void invoke() {
	        // 处理dog
	    }
	}
	public class CatStrategy implements Strategy {
	    @Override
	    public void invoke() {
	        // 处理cat
	    }
	}
	public class PigStrategy implements Strategy {
	    @Override
	    public void invoke() {
	        // 处理pig
	    }
	}
	public class RabbitStrategy implements Strategy {
	    @Override
	    public void invoke() {
	        // 处理rabbit
	    }
	}

具体的策略对象可以放在一个Map中,优化后的实现类似如下

	Strategy strategy = map.get(petType);
	stratefy.invoke();

关于如何存放到Map中也两个可以参考的方式。

3.1.1 静态表

	Map<String, Strategy> map = ImmutableMap.<String, Strategy>builder()
	    .put("dog", new DogStrategy())
	    .put("cat", new CatStrategy())
	    .put("pig", new PigStrategy())
	    .put("rabbit", new RabbitStrategy())
	    .build();

3.1.2 Spring托管下的动态注册

定义一个注册中心用于接受注册信息

	public enum StrategyMapping {
	    INSTANCE;
	    
	    private final Map<String, Class<? extends Strategy>> map = new ConcurrentHashMap<>();
	    
	    public void register(String type,  Class<? extends Strategy> clazz) {
	        map.put(type, clazz);
	    }
	    
	    public Strategy getStrategy(String type) {
	        Class<? extends Strategy> clazz = map.get(type);
	        if (clazz == null) {
	            throw new UnregisteredException();
	        }
	        return SpringContextHolder.getBean(clazz);
	    }
	}

将每个Strategy交由Spring管理,并在构造后注册

	@Component
	public class DogStrategy implements Strategy {
	    @PostConstruct
	    public void init() {
	        StrategyMapping.INSTANCE.register("dog", this.getClass());
	    }
	    
	    @Override
	    public void invoke() {
	        // 处理dog
	    }
	}

使用方式就变成了

	Strategy strategy = StrategyMapping.INSTANCE.getStrategy(petType);
	stratefy.invoke();

3.2 枚举

采用多态会额外产生很多策略类,如果我们已经预先将petType定义成了枚举,就会发现可以把Strategy中的invoke()方法放到枚举中,从而完成了一种映射关系。

	public enum PetType {
	    DOG {
	        @Override
	        public void invoke() {
	            // 处理dog
	        }
	    },
	    CAT {
	        @Override
	        public void invoke() {
	            // 处理cat
	        }
	    },
	    PIG {
	        @Override
	        public void invoke() {
	            // 处理pig
	        }
	    },
	    RABBIT {
	        @Override
	        public void invoke() {
	            // 处理rabbit
	        }
	    };
	 
	    public abstract void invoke();
	}

这样在调用时的代码就类似如下:

	PetType petType = PetType.valueOf(type.toUpperCase(Locale.ROOT));
	petType.invoke();

3.3 函数式简化策略

同样面对多态会额外产生很多策略类的问题,除了枚举我们还可以使用函数式的方式来改写,这里有个前提最好是策略的内容不会过于复杂,不然在代码的可读性上会比较差

同样我们会有一个map静态表,不过map里面存放的是lambda

	Map<String, Runnable> map = ImmutableMap.<String, Runnable>builder()
	    .put("dog", () -> {
	        // 处理dog
	    })
	    .put("cat", () -> {
	        // 处理cat
	    })
	    .put("pig", () -> {
	        // 处理pig
	    })
	    .put("rabbit", () -> {
	        // 处理rabbit
	    })
	    .build();

使用方式则变成了

	Runnable task = map.get(petType);
	task.run();
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。