運用設計模式告別項目中大量臃腫的if else

前言

之前寫過的一個老項目中,有這樣一個業務場景,比喻:一個外賣系統須要接入多家餐館,在外賣系統中返回每一個餐館的菜單列表 ,每一個餐館的菜單價格都須要不一樣的算法計算。算法

代碼中使用了大量的if else嵌套鏈接,一個類中數千行代碼(眼睛快看瞎...),並且隨着業務的擴展,接入的餐館會愈來愈多,每接入一個餐館都要增長一個 if else,滿屏幕密密麻麻的邏輯代碼,毫無可讀性。而後前段時間進行了代碼重構,使用了策略模式+工廠模式+反射代替了這整片的臃腫代碼,瞬間神清氣爽。後端

 

模擬原業務代碼

原代碼的簡單模擬實現,根據傳入的不一樣餐館編碼獲取對應的餐館類集合,每一個餐館菜單價格的算法都不一樣。每當須要新接入一家餐館時,都須要在此增長一個if else,中間加入一大長串的處理邏輯,當餐館愈來愈多的時候,代碼就變得愈來愈沉重,維護成本高。app

public List server(String hotelCode) {
    if ("HotelA".equals(hotelCode)) {
        //獲取數據
        List<HotelA> hotelList = new ArrayList<HotelA>() {
            {
                add(new HotelA("爆炒腰子", 100d, 0.8, null));
                add(new HotelA("紅燒腰子", 200d, 0.8, null));
                add(new HotelA("腰子刺身", 300d, 0.8, null));
            }
        };
        //邏輯計算 最終價格 = 原價 * 折扣
        hotelList.parallelStream().forEach(v -> v.setFinalPrice(v.getPrice() * v.getDiscount()));
        return hotelList;

    } else if ("HotelB".equals(hotelCode)) {
        //獲取數據
        List<HotelB> hotelList = new ArrayList<HotelB>() {
            {
                add(new HotelB("蘭州拉麪", 100d, 10d, null));
                add(new HotelB("落魄後端在線炒粉", 200d, 20d, null));
            }
        };
        //邏輯計算 最終價格 = 原價 - 優惠
        hotelList.parallelStream().forEach(v -> v.setFinalPrice(v.getPrice() - v.getPreferential()));
        return hotelList;

    } else if ("HotelC".equals(hotelCode)) {
        //獲取數據
        List<HotelC> hotelList = new ArrayList<HotelC>() {
            {
                add(new HotelC("祕製奧利給", 1000d, 0.6, 20d, null));
                add(new HotelC("老八辣醬", 2000d, 0.6, 10d, null));
            }
        };
        //邏輯計算 最終價格 = 原價 * 折扣 - 服務費
        hotelList.parallelStream().forEach(v -> v.setFinalPrice(v.getPrice() * v.getDiscount() - v.getTip()));
        return hotelList;
    }
    return new ArrayList();
}

 

@Data
@NoArgsConstructor
@AllArgsConstructor
public class HotelA {

    //菜品名
    private String menu;

    //原價
    private Double price;

    //折扣
    private Double discount;

    //最終價格 = 原價 * 折扣
    private Double finalPrice;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HotelB {

    //菜品名
    private String menu;

    //原價
    private Double price;

    //優惠
    private Double preferential;

    //最終價格 = 原價 - 優惠
    private Double finalPrice;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HotelC {

    //菜品名
    private String menu;

    //原價
    private Double price;

    //折扣
    private Double discount;

    //服務費
    private Double tip;

    //最終價格 = 原價 * 折扣 - 服務費
    private Double finalPrice;
}

 

策略模式+工廠模式+反射

由上述代碼首先抽離出一個接口,if else中的業務邏輯最終都是返回一個列表ide

/**
 * 餐館服務接口
 */
public interface HotelService {

    /**
     * 獲取餐館菜單列表
     * @return
     */
    List getMenuList();
}

 

 

把每一個分支的業務邏輯封裝成實現類,實現HotelService接口this

public class HotelAServiceImpl implements HotelService {

    /**
     * 邏輯計算 返回集合
     * @return
     */
    @Override
    public List getMenuList() {
        return initList().parallelStream()
                .peek(v -> v.setFinalPrice(v.getPrice() * v.getDiscount()))
                .collect(Collectors.toList());
    }

    /**
     * 獲取數據
     * @return
     */
    public List<HotelA> initList() {
        return new ArrayList<HotelA>() {
            {
                add(new HotelA("爆炒腰子", 100d, 0.8, null));
                add(new HotelA("紅燒腰子", 200d, 0.8, null));
                add(new HotelA("腰子刺身", 300d, 0.8, null));
            }
        };
    }
}
public class HotelBServiceImpl implements HotelService {

    /**
     * 邏輯計算 返回集合
     * @return
     */
    @Override
    public List getMenuList() {
        return initList().parallelStream()
                .peek(v -> v.setFinalPrice(v.getPrice() - v.getPreferential()))
                .collect(Collectors.toList());
    }

    /**
     * 獲取數據
     * @return
     */
    public List<HotelB> initList() {
        return new ArrayList<HotelB>() {
            {
                add(new HotelB("蘭州拉麪", 100d, 10d, null));
                add(new HotelB("落魄後端在線炒粉", 200d, 20d, null));
            }
        };
    }
}
public class HotelCServiceImpl implements HotelService {

    /**
     * 邏輯計算 返回集合
     * @return
     */
    @Override
    public List getMenuList() {
        return initList().parallelStream()
                .peek(v -> v.setFinalPrice(v.getPrice() * v.getDiscount() - v.getTip()))
                .collect(Collectors.toList());
    }

    /**
     * 獲取數據
     * @return
     */
    public List<HotelC> initList() {
        return new ArrayList<HotelC>() {
            {
                add(new HotelC("祕製奧利給", 1000d, 0.6, 20d, null));
                add(new HotelC("老八辣醬", 2000d, 0.6, 10d, null));
            }
        };
    }
}

 

 

這樣就是一個簡單的策略模式了,可是如今要調用不一樣的實現類中的getMenuList方法,好像仍是離不開if else,那麼如今就須要用工廠模式把全部實現類包裝起來。編碼

先定義一個枚舉類,裏面是各餐館的codespa

public enum HotelEnum {

    HOTEL_A("HotelA"),
    HOTEL_B("HotelB"),
    HOTEL_C("HotelC"),;

    private String hotelCode;

    /**
     * 返回全部餐館編碼的集合
     * @return
     */
    public static List<String> getList() {
        return Arrays.asList(HotelEnum.values())
                .stream()
                .map(HotelEnum::getHotelCode)
                .collect(Collectors.toList());
    }

    HotelEnum(String hotelCode) {
        this.hotelCode = hotelCode;
    }

    public String getHotelCode() {
        return hotelCode;
    }

}

 

 

接下來定義一個服務工廠,在靜態塊中利用反射機制把全部服務實現類動態加載到HOTEL_SERVER_MAP中,而後提供一個對外的獲取對應服務的方法code

這裏有幾個須要注意的地方:server

1.因爲包名是寫死的,那麼全部實現HotelService的實現類都須要放在固定的包下對象

2.類名的格式也是固定的,即枚舉類中的hotelCode + "ServiceImpl"

/**
 * 服務工廠類
 */
public class HotelServerFactory {
    /**
     * 類路徑目錄
     */
    private static final String CLASS_PATH = "com.tactics.service.impl.";

    /**
     * 服務實現後綴
     */
    private static final String GAME_SERVICE_SUFFIX = "ServiceImpl";


    private static final Map<String, HotelService> HOTEL_SERVER_MAP = new ConcurrentHashMap<>();

    /**
     * 初始化實現類到COMPANY_SERVER_MAP中
     */
    static {
        HotelEnum.getList().forEach(v -> {
            String className = CLASS_PATH + v + GAME_SERVICE_SUFFIX;
            try {
                HOTEL_SERVER_MAP.put(v, (HotelService) Class.forName(className).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 獲取餐館服務實現
     *
     * @param hotelCode
     * @return
     */
    public static HotelService getHotelServerImpl(String hotelCode) {
        return HOTEL_SERVER_MAP.get(hotelCode);
    }
}

 

這裏有一個問題,若是你的服務實現類是交給Spring容器管理的,裏面有注入Mapper等等,使用反射的方式new出來的話,其中的屬性是沒有值的

Spring容器就至關因而一個工廠了,能夠直接從Spring上下文中獲取(怎麼獲取Spring上下文對象這裏就不細說了,有須要能夠自行百度)。

/**
 * 服務工廠類
 */
public class HotelServerFactory {
    /**
     * 類路徑目錄
     */
    private static final String CLASS_PATH = "com.tactics.service.impl.";

    /**
     * 服務實現後綴
     */
    private static final String GAME_SERVICE_SUFFIX = "ServiceImpl";

    /**
     * 獲取餐館服務實現
     *
     * @param hotelCode
     * @return
     */
    public static HotelService getHotelServerImpl(String hotelCode) {
        Class clazz = Class.forName(CLASS_PATH + hotelCode + GAME_SERVICE_SUFFIX);
        String className = hotelCode + GAME_SERVICE_SUFFIX;
        return (HotelService) ApplicationConfig.getBean(className, clazz);
    }
}

 

 

最終的調用

public List server(String hotelCode) {
    //獲取對應的服務
    HotelService hotelService = HotelServerFactory.getCompanyServerImpl(hotelCode);
    //獲取通過邏輯計算後返回的集合列表
    return hotelService.getMenuList();
}

 

 

怎麼樣,是否是以爲可讀性,複用性和擴展性都大大提升了,業務擴展須要新加一個餐館的時候,只須要在枚舉類中加一個hotelCode,而後定義一個實現類實現HotelService接口就行了,這個Demo也讓咱們知道了策略模式和工廠模式在實際項目中的應用場景。

相關文章
相關標籤/搜索