如何少寫if-else
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 value1
} else {
// return value2
}
使用Optional後
Optional.ofNullable(input).map(value1).orElse(value2);
2.2簡化多級判空
假設有如下程式碼
if (input != null && input.getUser() != null && input.getUser().getName() != null) {
// return value1
} else {
// return value2
}
使用Optional後
return Optional.ofNullable(input) .map(Input::getUser) .map(User::getName) .map(value1) .orElse(value2);
對於沒有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.2Spring託管下的動態註冊
(1) 定義一個註冊中心用於接受註冊資訊
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);
}
}
(2) 將每個Strategy交由Spring管理,並在構造後註冊
@Component
public class DogStrategy implements Strategy {
@PostConstruct
public void init() {
StrategyMapping.INSTANCE.register("dog", this.getClass());
}
@Override
public void invoke() {
// 處理dog
}
}
(3) 使用方式就變成了
Strategy strategy = StrategyMapping.INSTANCE.getStrategy(petType);
stratefy.invoke();
3.1.3Spring託管下的註解繫結
如果你不想在每個bean裡面使用@PostConstruct去註冊,你還可以使用註解來完成繫結並註冊
(1) 先宣告一個註解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface StrategyMapping {
String type();
}
(2) 在bean上繫結註解
@StrategyMapping(type = "dog")
@Component
public class DogStrategy implements Strategy {
@Override
public void invoke() {
// 處理dog
}
}
(3) 增加一個監聽器用於註冊
@Component
public class StrategyAnnotationRegistry implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
registerStrategy(event);
}
private void registerStrategy(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
Map<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(StrategyMapping.class);
beansWithAnnotation.forEach((key, value) -> {
String type = AnnotationUtils.findAnnotation(value.getClass(), StrategyMapping.class).type();
if (value instanceof Strategy) {
StrategyMapping.INSTANCE.register(type, (Strategy) value);
}
});
}
}
(4) 註冊中心我們就可以不存class了,存實際的bean
public enum StrategyMapping {
INSTANCE;
private final Map<String, Strategy> map = new ConcurrentHashMap<>();
public void register(String type, Strategy bean) {
map.put(type, bean);
}
public Strategy getStrategy(String type) {
Strategy bean = map.get(type);
if (bean == null) {
throw new UnregisteredException();
}
return bean;
}
}
(5) 使用方式與之前一致
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();
4.責任鏈模式
與策略模式類似,假設我們已經將不同的業務邏輯都抽離成了單獨的Strategy類,即我們有了DogStrategy
,CatStrategy
,PigStrategy
,RabbitStrategy
,在3.策略模式中,我們主要是使用了表驅動
的方式,得到了petType和Strategy的對映關係,在執行之前獲取了既定的Strategy。
換一種思路,如果我們不從外部去維護這種對映關係,而是讓各個Strategy自己去判斷是否執行,這樣只需維護一個Strategy組成的List,然後逐個請求、嘗試執行即可。
不過這種方式,在Strategy較多的情況下,遍歷List的效能要比表驅動差。
這種類似責任鏈的方式,也有兩種體現方式,主要差異在於外部迭代還是內部迭代。
4.1 外部迭代
和3.1多型類似,我們定義一套策略,不過介面上有些差異
public interface Strategy {
boolean isSupport(String petType); // 是否由當前策略處理
void invoke(); // 處理各個邏輯
}
public class DogStrategy implements Strategy {
@Override
public boolean isSupport(String petType) {
return "dog".equals(petType);
}
@Override
public void invoke() {
// 處理dog
}
}
public class CatStrategy implements Strategy {
@Override
public boolean isSupport(String petType) {
return "cat".equals(petType);
}
@Override
public void invoke() {
// 處理cat
}
}
public class PigStrategy implements Strategy {
@Override
public boolean isSupport(String petType) {
return "pig".equals(petType);
}
@Override
public void invoke() {
// 處理pig
}
}
public class RabbitStrategy implements Strategy {
@Override
public boolean isSupport(String petType) {
return "rabbit".equals(petType);
}
@Override
public void invoke() {
// 處理rabbit
}
}
然後我們維護一個由Strategy組成的List,這裡同樣可以採用靜態註冊
或動態註冊
的方式,這裡以靜態註冊
為例:
List<Strategy> list = ImmutableList.<Strategy>builder()
.put(new DogStrategy())
.put(new CatStrategy())
.put(new PigStrategy())
.put(new RabbitStrategy())
.build();
呼叫的方式則變成了
for (Strategy strategy : list) {
if (strategy.isSupport(petType)) {
strategy.invoke();
}
}