優化if...else...語句

2023-01-16 18:00:20

寫程式碼的時候經常遇到這樣的場景:根據某個欄位值來進行不同的邏輯處理。例如,不同的會員等級在購物時有不同的折扣力度。如果會員的等級很多,那麼程式碼中與之相關的if...elseif...else...會特別長,而且每新增一種等級時需要修改原先的程式碼。可以用策略模式來優化,消除這種場景下的if...elseif...else...,使程式碼看起來更優雅。

首先,定義一個介面

/**
 * 會員服務
 */
public interface VipService {
    void handle();
}

然後,定義實現類

/**
 * 白銀會員
 */
public class SilverVipService implements VipService {
    @Override
    public void handle() {
        System.out.println("白銀");
    }
}

/**
 * 黃金會員
 */
public class GoldVipService implements VipService {
    @Override
    public void handle() {
        System.out.println("黃金");
    }
}

最後,定義一個工廠類,目的是當傳入一個會員等級後,返回其對應的處理類

public class VipServiceFactory {
    private static Map<String, VipService> vipMap = new ConcurrentHashMap<>();

    public static void register(String type, VipService service) {
        vipMap.put(type, service);
    }

    public static VipService getService(String type) {
        return vipMap.get(type);
    }
}

為了建立會員等級和與之對應的處理類之間的對映關係,這裡通常可以有這麼幾種處理方式:

方式一:實現類手動註冊

可以實現InitializingBean介面,或者在某個方法上加@PostConstruct註解

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

/**
 * 白銀會員
 */
@Component
public class SilverVipService implements VipService, InitializingBean {
    @Override
    public void handle() {
        System.out.println("白銀");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        VipServiceFactory.register("silver", this);
    }
}

/**
 * 黃金會員
 */
@Component
public class GoldVipService implements VipService, InitializingBean {
    @Override
    public void handle() {
        System.out.println("黃金");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        VipServiceFactory.register("gold", this);
    }
}

方式二:從Spring容器中直接獲取Bean

public interface VipService {
    void handle();
    String getType();
}

/**
 * 白銀會員
 */
@Component
public class SilverVipService implements VipService {
    @Override
    public void handle() {
        System.out.println("白銀");
    }
    @Override
    public String getType() {
        return "silver";
    }
}

/**
 * 黃金會員
 */
@Component
public class GoldVipService implements VipService {
    @Override
    public void handle() {
        System.out.println("黃金");
    }
    @Override
    public String getType() {
        return "gold";
    }
}

/**
 * 上下文
 */
@Component
public class VipServiceFactory implements ApplicationContextAware {
    private static Map<String, VipService> vipMap = new ConcurrentHashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, VipService> map = applicationContext.getBeansOfType(VipService.class);
        map.values().forEach(service -> vipMap.put(service.getType(), service));
    }

    public static VipService getService(String type) {
        return vipMap.get(type);
    }
}

/**
 * 測試
 */
@SpringBootTest
class DemoApplicationTests {
    @Test
    void contextLoads() {
        VipServiceFactory.getService("silver").handle();
    }
}

方式三:反射+自定義註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MemberLevel {
    String value();
}

@MemberLevel("silver")
@Component
public class SilverVipService implements VipService {
    @Override
    public void handle() {
        System.out.println("白銀");
    }
}

@MemberLevel("gold")
@Component
public class GoldVipService implements VipService {
    @Override
    public void handle() {
        System.out.println("黃金");
    }
}

/**
 * 上下文
 */
@Component
public class VipServiceFactory implements ApplicationContextAware {
    private static Map<String, VipService> vipMap = new ConcurrentHashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//        Map<String, VipService> map = applicationContext.getBeansOfType(VipService.class);
        Map<String, Object> map = applicationContext.getBeansWithAnnotation(MemberLevel.class);
        for (Object bean : map.values()) {
            if (bean instanceof VipService) {
                String type = bean.getClass().getAnnotation(MemberLevel.class).value();
                vipMap.put(type, (VipService) bean);
            }
        }
    }

    public static VipService getService(String type) {
        return vipMap.get(type);
    }
}

完整範例程式碼

/**
 * 結算業務種類
 * @Author: ChengJianSheng
 * @Date: 2023/1/16
 */
@Getter
public enum SettlementBusiType {
    RE1011("RE1011", "轉貼現"),
    RE4011("RE4011", "買斷式貼現"),
    RE4021("RE4021", "回購式貼現"),
    RE4022("RE4022", "回購式貼現贖回");
//    ......

    private String code;
    private String name;

    SettlementBusiType(String code, String name) {
        this.code = code;
        this.name = name;
    }
}


/**
 * 結算處理器
 * @Author: ChengJianSheng
 * @Date: 2023/1/16
 */
public interface SettlementHandler {
    /**
     * 清算
     */
    void handle();

    /**
     * 獲取業務種類
     */
    SettlementBusiType getBusiType();
}


/**
 * 轉貼現結算處理
 */
@Component
public class RediscountSettlementHandler implements SettlementHandler {
    @Override
    public void handle() {
        System.out.println("轉貼現");
    }

    @Override
    public SettlementBusiType getBusiType() {
        return SettlementBusiType.RE1011;
    }
}


/**
 * 買斷式貼現結算處理
 */
@Component
public class BuyoutDiscountSettlementHandler implements SettlementHandler {
    @Override
    public void handle() {
        System.out.println("買斷式貼現");
    }

    @Override
    public SettlementBusiType getBusiType() {
        return SettlementBusiType.RE4011;
    }
}


/**
 * 預設處理器
 * @Author: ChengJianSheng
 * @Date: 2023/1/16
 */
@Component
public class DefaultSettlementHandler implements /*SettlementHandler,*/ ApplicationContextAware {
    private static Map<SettlementBusiType, SettlementHandler> allHandlerMap = new ConcurrentHashMap<>();

    public static SettlementHandler getHandler(SettlementBusiType busiType) {
        return allHandlerMap.get(busiType);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, SettlementHandler> map = applicationContext.getBeansOfType(SettlementHandler.class);
        map.values().forEach(e -> allHandlerMap.put(e.getBusiType(), e));
    }
}


@SpringBootTest
class Demo2023ApplicationTests {
    @Test
    void contextLoads() {
        // 收到結算結果通知,根據業務種類進行結算處理
        SettlementHandler handler = DefaultSettlementHandler.getHandler(SettlementBusiType.RE1011);
        if (null != handler) {
            handler.handle();
        }
    }
}