1. 背景
最近在幫老師做項目,讓我重構一個項目,看到項目的代碼後,真是小刀拉屁股——開了眼了。一個方法,三千多行,一堆if-else的判斷條件,而且變量的命名,居然是a,b,c,d,xx,yy這種格式的,看了老半天,沒看懂在幹啥,最後咬咬牙,按照之前的文檔,重新寫了一個方法,三千多行的代碼,實際上有66%以上的代碼是重復的,就是復制某個if塊的邏輯,到另外一個if塊中,因此寫下這篇文章,記錄一下在項目中,如何減少if-else的出現。
![](https://news.xinpengboligang.com/upload/keji/f39574a8ae949573b813468f3fe73bc3.jpeg)
2. 常用優化方式
2.1. 去除不必要的else
如果if-else代碼塊中,隻有一個判斷條件,可以把多餘else幹掉,使代碼更整潔
優化前:
if(condition){
// dosomething1
return;
} else {
// dosomething2
return;
}
優化後:
if(condition){
// dosomething1
return;
}
// dosomething2
return;
2.2. 使用三目運算符
對於某些if-else,可以使用三目運算符來進行簡化,使代碼更簡潔
優化前:
int price;
if(condition){
price = 80;
} else {
price = 100;
}
優化後:
int price = condition ? 80 : 100;
2.3. 使用Optional優化判空
在項目中,很多時候會用到if來判斷空指針,這個我們可以使用Optional進行優化
優化前:
Person p = new Person();
if(address != null) {
p.setAddress(address);
}
if(phone != null) {
p.setPhone(phone);
}
優化後:
Person p = new Person();
Optional.ofNullable(address).ifPresent(Person::setAddress);
Optional.ofNullable(phone).ifPresent(Person::setPhone);
2.4. 使用枚舉
在項目中,有時候要根據某個狀態值,設置對應的屬性,這個其實可以使用枚舉來代替。
優化前:
Order order = new Order();
if(status == 1) {
order.setStatus("支付中");
} else if (status == 2) {
order.setStatus("已支付");
} else if (status == 3) {
...
} else {
...
}
優化後:
// 定義一個枚舉類
public enum OrderStatus {
PAYIING(1, "支付中"),
PAID(2, "已支付"),
...;
private int code;
private String status;
OrderStatus(int code, String status) {
this.code = code;
this.status = status;
}
public Orderstatus getOrderByCode(int code) {
...
}
}
Order order = new Order();
OrderStatus orderStatus = OrderStatus.getOrderByCode(status);
order.setStatus(orderStatus.getStatus());
2.5. 策略模式 工廠模式
對於if-else代碼塊中,內容比較復雜的時候,我們可以使用策略模式 工廠模式來進行改造,以減少代碼塊中的if-else。
策略模式:一種解耦的方法,它對算法進行封裝,使得算法的調用和算法本身分離,使用策略模式客戶端代碼不需要調整,算法之間可以互相替換,因為不同的算法實現的是同一個接口。策略模式是一種對象行為型模式,策略模式符合“開閉原則”。
策略模式包含如下角色:
1)Context:環境類
2)Strategy:抽象策略類
3)ConcreteStrategy:具體策略類
業務場景:在我們的微信錢包中,可以綁定多張銀行卡,假設綁定銀行卡時,銀行卡的提供商會對綁定行為進行風控(假設不同銀行卡的風控行為不同)。
2.5.1. 改造前
//銀行卡父類Card.java
package com.young.pojo;
import lombok.Data;
@Data
public class Card {
private String cardNo;
private String bankCode;
private String bankName;
private Long registerTime;
private Integer balance;
}
//農業銀行卡
package com.young.pojo;
public class AgriculturalCard extends Card{
}
//建設銀行卡
package com.young.pojo;
public class BuilderCard extends Card{
}
//郵政銀行卡
package com.young.pojo;
public class PostalCard extends Card{
}
//銀行卡枚舉類
package com.young.enums;
public enum CardEnum {
BUILDER,
AGRICULTURAL,
POSTAL;
}
//銀行卡風控請求類
package com.young.request;
import lombok.Data;
@Data
public class CardRiskRequest {
private String cardNo;
private String bankCode;
private Long registerTime;
private String userName;
}
//銀行卡風控響應類
package com.young.response;
import lombok.Data;
@Data
public class CardRiskResponse {
private Boolean result;
private String unableReason;
private String challengeType;
}
銀行卡風控處理的service:
package com.young.service.impl;
import com.young.enums.CardEnum;
import com.young.request.CardRiskRequest;
import com.young.response.CardRiskResponse;
import com.young.service.CardRiskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Component
@Slf4j
public class EasyCardRiskService implements CardRiskService {
@Override
public CardRiskResponse consultRisk(CardRiskRequest cardRiskRequest) {
if (cardRiskRequest.getBankCode().equals(CardEnum.AGRICULTURAL.name())){
return agriculturalRisk(cardRiskRequest);
}else if (cardRiskRequest.getBankCode().equals(CardEnum.BUILDER.name())){
return builderRisk(cardRiskRequest);
} else if (cardRiskRequest.getBankCode().equals(CardEnum.POSTAL.name())) {
return postalRisk(cardRiskRequest);
}
throw new UnsupportedOperationException();
}
private CardRiskResponse postalRisk(CardRiskRequest cardRiskRequest) {
log.info("郵政銀行判斷綁卡是否需要風控");
CardRiskResponse cardRiskResponse=new CardRiskResponse();
if (cardRiskRequest.getUserName().equals("cxy")){
cardRiskResponse.setResult(true);
}else{
cardRiskResponse.setResult(false);
cardRiskResponse.setChallengeType("OTP");
cardRiskResponse.setUnableReason("用戶信息需要風控");
}
return cardRiskResponse;
}
private CardRiskResponse builderRisk(CardRiskRequest cardRiskRequest) {
log.info("建設銀行判斷綁卡是否需要風控");
CardRiskResponse cardRiskResponse=new CardRiskResponse();
if (cardRiskRequest.getRegisterTime()%2==0){
cardRiskResponse.setResult(true);
}else{
cardRiskResponse.setResult(false);
cardRiskResponse.setChallengeType("OTP");
cardRiskResponse.setUnableReason("註冊時間需要驗證");
}
return cardRiskResponse;
}
private CardRiskResponse agriculturalRisk(CardRiskRequest cardRiskRequest) {
log.info("農業銀行判斷綁卡是否需要風控");
CardRiskResponse cardRiskResponse=new CardRiskResponse();
if (cardRiskRequest.getCardNo().startsWith("1000")){
cardRiskResponse.setResult(true);
}else{
cardRiskResponse.setResult(false);
cardRiskResponse.setChallengeType("OTP");
cardRiskResponse.setUnableReason("不知名原因");
}
return cardRiskResponse;
}
}
2.5.2. 改造後
上面的EasyCardRiskService這個類中,存在大量if...else...,當我們要支持其他銀行卡時,必須在這個service中進行代碼改動,違反開閉原則。
下面我們用策略模式結合工廠模式進行改造。
首先定義一個風控接口
package com.young.service;
import com.young.request.CardRiskRequest;
import com.young.response.CardRiskResponse;
public interface CardRiskService {
default CardRiskResponse consultRisk(CardRiskRequest cardRiskRequest){
throw new UnsupportedOperationException();
}
}
依次對農業銀行、建設銀行和郵政銀行進行實現
//AgriculturalCardRiskService
package com.young.service.impl;
import com.young.enums.CardEnum;
import com.young.factory.CardRiskServiceFactory;
import com.young.request.CardRiskRequest;
import com.young.response.CardRiskResponse;
import com.young.service.CardRiskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Component
@Slf4j
public class AgriculturalCardRiskService implements CardRiskService, InitializingBean {
@Override
public CardRiskResponse consultRisk(CardRiskRequest cardRiskRequest) {
log.info("農業銀行判斷綁卡是否需要風控");
CardRiskResponse cardRiskResponse=new CardRiskResponse();
if (cardRiskRequest.getCardNo().startsWith("1000")){
cardRiskResponse.setResult(true);
}else{
cardRiskResponse.setResult(false);
cardRiskResponse.setChallengeType("OTP");
cardRiskResponse.setUnableReason("不知名原因");
}
return cardRiskResponse;
}
@Override
public void afterPropertiesSet() throws Exception {
CardRiskServiceFactory.put(CardEnum.AGRICULTURAL.name(), this);
}
}
//BuilderCardRiskService
package com.young.service.impl;
import com.young.enums.CardEnum;
import com.young.factory.CardRiskServiceFactory;
import com.young.request.CardRiskRequest;
import com.young.response.CardRiskResponse;
import com.young.service.CardRiskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Component
@Slf4j
public class BuilderCardRiskService implements CardRiskService , InitializingBean {
@Override
public CardRiskResponse consultRisk(CardRiskRequest cardRiskRequest) {
log.info("建設銀行判斷綁卡是否需要風控");
CardRiskResponse cardRiskResponse=new CardRiskResponse();
if (cardRiskRequest.getRegisterTime()%2==0){
cardRiskResponse.setResult(true);
}else{
cardRiskResponse.setResult(false);
cardRiskResponse.setChallengeType("OTP");
cardRiskResponse.setUnableReason("註冊時間需要驗證");
}
return cardRiskResponse;
}
@Override
public void afterPropertiesSet() throws Exception {
CardRiskServiceFactory.put(CardEnum.BUILDER.name(), this);
}
}
//PostCardRiskService
package com.young.service.impl;
import com.young.enums.CardEnum;
import com.young.factory.CardRiskServiceFactory;
import com.young.request.CardRiskRequest;
import com.young.response.CardRiskResponse;
import com.young.service.CardRiskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Component
@Slf4j
public class PostalCardRiskService implements CardRiskService, InitializingBean {
@Override
public CardRiskResponse consultRisk(CardRiskRequest cardRiskRequest) {
log.info("郵政銀行判斷綁卡是否需要風控");
CardRiskResponse cardRiskResponse=new CardRiskResponse();
if (cardRiskRequest.getUserName().equals("cxy")){
cardRiskResponse.setResult(true);
}else{
cardRiskResponse.setResult(false);
cardRiskResponse.setChallengeType("OTP");
cardRiskResponse.setUnableReason("用戶信息需要風控");
}
return cardRiskResponse;
}
@Override
public void afterPropertiesSet() throws Exception {
CardRiskServiceFactory.put(CardEnum.POSTAL.name(), this);
}
}
創建CardRiskService工廠類
package com.young.factory;
import com.young.service.CardRiskService;
import org.springframework.util.ConcurrentReferenceHashMap;
import java.util.Map;
public class CardRiskServiceFactory {
private static Map<String, CardRiskService>cardRiskServiceMap=new ConcurrentReferenceHashMap<>();
public static CardRiskService getInstance(String bankCode){
return cardRiskServiceMap.get(bankCode);
}
public static void put(String bankCode,CardRiskService cardRiskService){
cardRiskServiceMap.put(bankCode,cardRiskService);
}
}