《設計模式面試小炒》策略和工廠模式替代業(yè)務場景中復雜的ifelse

我是肥哥上鞠,一名不專業(yè)的面試官!

我是囧囧芯丧,一名積極找工作的小菜鳥芍阎!

囧囧表示:小白面試最怕的就是面試官問的知識點太籠統(tǒng),自己無法快速定位到關鍵問題點SШ恪G聪獭!


本期主要面試考點

面試官考點之如何用設計模式替換業(yè)務場景中復雜的ifelse肿轨?

1
2

VIP類型

import java.util.Objects;

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 *
 * 會員類型
 */
public enum VIPEnums {

    GOLD(1, "黃金會員"),
    STAR(2, "星鉆會員"),
    SPORTS(3, "體育會員"),
    FUN_VIP(4, "FUN會員");

    private final int code;
    private final String desc;

    VIPEnums(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }

    public static VIPEnums getByCode(Integer code) {
        for (VIPEnums s : VIPEnums.values()) {
            if (Objects.equals(s.getCode(), code)) {
                return s;
            }
        }
        return null;
    }
}

VIP實體

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmeifeishi@163.com
 *
 * vip
 */
public class VIP {

    private VIPEnums vipType;

    // TODO VIP 其他屬性 id, name ...

    public VIP() {
    }

    public VIP(VIPEnums vipType) {
        this.vipType = vipType;
    }

    public VIPEnums getVipType() {
        return vipType;
    }

    public void setVipType(VIPEnums vipType) {
        this.vipType = vipType;
    }
}

if-else 模式

// if-else 模式
public class App {
    public static void main( String[] args ) {

        // 黃金會員
        VIP vip = new VIP(VIPEnums.GOLD);

        if (vip.getVipType().getCode() == VIPEnums.GOLD.getCode()) {
            // TODO 黃金會員權益
        } else if (vip.getVipType().getCode() == VIPEnums.STAR.getCode()) {
            // TODO 星鉆會員權益
        } else if (vip.getVipType().getCode() == VIPEnums.SPORTS.getCode()) {
            // TODO 體育會員權益
        } else if (vip.getVipType().getCode() == VIPEnums.FUN_VIP.getCode()) {
            // TODO FUN會員權益
        } else {
            // TODO 其他會員...
        }

    }
}

策略模式

VIP策略接口

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 * VIP 策略接口
 */
public interface VIPStrategy {

    // VIP 具備的權益
    void equity();
}

策略接口具體實現(xiàn)類-黃金會員

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 *
 * 策略接口具體實現(xiàn)類-黃金會員
 */
public class GoldVIPStrategyImpl implements VIPStrategy {

    @Override
    public void equity() {
        // TODO 黃金會員具備的具體權益
    }
}

策略接口具體實現(xiàn)類-星鉆會員

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 *
 * 策略接口具體實現(xiàn)類-星鉆會員
 */
public class StarVIPStrategyImpl implements VIPStrategy {

    @Override
    public void equity() {
        // TODO 星鉆會員具備的具體權益
    }
}

策略接口具體實現(xiàn)類-體育會員

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 *
 * 策略接口具體實現(xiàn)類-體育會員
 */
public class SportsVIPStrategyImpl implements VIPStrategy {

    @Override
    public void equity() {
        // TODO 體育會員具備的具體權益
    }
}

策略接口具體實現(xiàn)類-FUN會員

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 *
 * 策略接口具體實現(xiàn)類-FUN會員
 */
public class FunVIPStrategyImpl implements VIPStrategy {

    @Override
    public void equity() {
        // TODO FUN會員具備的具體權益
    }
}

策略上下文類

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 *
 * 策略上下文類( vip 策略接口的持有者)
 */
public class VIPStrategyContext {

    private VIPStrategy vipStrategy;

    // 設置VIP策略
    public void setVipStrategy(VIPStrategy vipStrategy) {
        this.vipStrategy = vipStrategy;
    }

    // 執(zhí)行 VIP 權益
    public void handle() {
        if (vipStrategy != null) {
            vipStrategy.equity();
        }
    }
}

策略工廠

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 *
 * VIP策略工廠
 */
public class VIPStrategyFactory {

    private VIPStrategyFactory() {
    }

    public static VIPStrategy getVipStrategy(VIP vip) {
        VIPStrategy vipStrategy = null;
        
        if (vip.getVipType().getCode() == VIPEnums.GOLD.getCode()) {
            // 黃金會員策略實現(xiàn)類
            vipStrategy = new GoldVIPStrategyImpl();
        } else if (vip.getVipType().getCode() == VIPEnums.STAR.getCode()) {
            // 星鉆會員策略實現(xiàn)類
            vipStrategy = new StarVIPStrategyImpl();
        } else if (vip.getVipType().getCode() == VIPEnums.SPORTS.getCode()) {
            // 體育會員策略實現(xiàn)類
            vipStrategy = new SportsVIPStrategyImpl();
        } else if (vip.getVipType().getCode() == VIPEnums.FUN_VIP.getCode()) {
            // FUN會員策略實現(xiàn)類
            vipStrategy = new FunVIPStrategyImpl();
        } else {
            // 其他會員...
        }

        return vipStrategy;
    }
}

模擬會員登錄獲取權益

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 *
 * 模擬會員登錄獲取權益
 */
public class TestStrategy {

    public static void main(String[] args) {

        // 黃金會員
        VIP vip = new VIP(VIPEnums.GOLD);

        // 策略上下文寿冕,執(zhí)行者
        VIPStrategyContext context = new VIPStrategyContext();

        // 根據(jù)會員類型,獲取會員具體策略椒袍,獲取黃金會員策略
        VIPStrategy strategy = VIPStrategyFactory.getVipStrategy(vip);

        // 綁定給執(zhí)行者
        context.setVipStrategy(strategy);

        // 執(zhí)行黃金會員的策略驼唱,黃金權益
        context.handle();
    }
}

我們知道, 策略模式的本身設計出來的目的是封裝一系列的算法驹暑,這些算法都具有共性玫恳,可以相互替換,算法獨立于使用它的客戶端獨立變化优俘,客戶端不需要了解關注算法的具體實現(xiàn)京办,客戶端僅僅依賴于策略接口 。

通過使用策略模式和工廠模式結(jié)合帆焕,是不是感覺變得高大上起來了呢惭婿???

當然了,最主要的是程序的擴展來說更方便了一些叶雹,更符合開閉原則财饥,開放擴展,關閉修改折晦。無論新增多少種新類型的會員钥星,每個人只需要去繼承策略接口,實現(xiàn)新會員應有的權益即可满着。

注意谦炒,雖然利于擴展,但是策略模式的缺點也很明顯风喇,策略工廠在創(chuàng)建具體的策略實現(xiàn)類的時候宁改,還是書寫大量的 if-else 去進行判斷,如圖

缺點

有小伙伴就說了這和不使用策略模式和工廠模式似乎差不多响驴?透且??

抽出一個方法或者封裝成一個對象去調(diào)用豈不是更簡單豁鲤?秽誊??

抽調(diào)

接下來琳骡,我們就說說如何優(yōu)化策略工廠锅论。

首先,我們的工廠楣号,是根據(jù)當前傳入的用戶的會員類型最易,判斷后,返回相應的策略實現(xiàn)類炫狱,那么可以借助集合來存儲實現(xiàn)類藻懒,會員類型作為 key,將所有的會員策略都注冊到 map 中视译。需要注意的是嬉荆,日常開發(fā)基于Spring進行bean管理,上面需要創(chuàng)建的策略類酷含,當然都是希望被 Spring 動態(tài)托管鄙早,而不是我們自己去一個個的new 出實例。

問題是椅亚,如何去實現(xiàn)策略類通過spring進行托管注冊?

Spring種提供的InitializingBean接口限番,這個接口為Bean提供了屬性初始化后的處理方法,它只包括afterPropertiesSet方法呀舔,凡是繼承該接口的類弥虐,在bean的屬性初始化后都會執(zhí)行該方法。我們利用此方法把Spring通過IOC創(chuàng)建出來的Bean注冊Map中媚赖。

改造策略工廠


import org.example.model.VIP;
import org.example.strategy.VIPStrategy;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 * <p>
 * VIP策略工廠
 */
public class VIPStrategyFactory {

    // 存儲策略類實例
    public static Map<Integer, VIPStrategy> strategyMap = new ConcurrentHashMap<>();

    private VIPStrategyFactory() {
    }

    public static VIPStrategy getVipStrategy(VIP vip) {
        if (vip == null) {
            return null;
        }
        return strategyMap.get(vip.getVipType().getCode());
    }
}

改造策略類霜瘪,在bean屬性初始化后,將實例對象注冊到工廠類中的 map

以黃金會員為例:

import org.example.factory.VIPStrategyFactory;
import org.example.model.VIPEnums;
import org.example.strategy.VIPStrategy;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

/**
 * @author: 歡迎關注喂信公豬號:囧么肥事
 * @date: 2021/12/16
 * @email: jiongmefeishi@163.com
 *
 * 策略接口具體實現(xiàn)類-黃金會員
 */
@Service
public class GoldVIPStrategyImpl implements VIPStrategy, InitializingBean {

    @Override
    public void equity() {
        // TODO 黃金會員具備的具體權益
        System.out.println("黃金會員具備的具體權益");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        VIPStrategyFactory.strategyMap.put(VIPEnums.GOLD.getCode(), new GoldVIPStrategyImpl());
    }
}

通過策略模式省古、工廠模式以及Spring的InitializingBean接口粥庄,算是解決了大量的if else,后續(xù)新VIP出現(xiàn)也更容易擴展豺妓,當然了惜互,這里只是對于設計模式思想的一個簡單的示例,實際應用開發(fā)中琳拭,還是要根據(jù)具體的業(yè)務場景靈活變通训堆。有需要的小伙伴也可以自己手動模擬一些場景,比如奶茶店各種奶茶新品等等白嘁。如果想用囧囧的示例坑鱼,可公豬號上回復220110 自行導入示例運行即可。

注意:學習軟件設計原則,千萬不能形成強迫癥鲁沥。當碰到業(yè)務復雜的場景時呼股,需要隨機應變。

學習設計原則是學習設計模式的基礎画恰。在實際開發(fā)過程中彭谁,并不是一定要求所有代碼都遵循設計原則,而是要綜合考慮人力允扇、時間缠局、成本、質(zhì)量考润,不刻意追求完美狭园,要在適當?shù)膱鼍白裱O計原則。這體現(xiàn)的是一種平衡取舍糊治,可以幫助我們設計出更加優(yōu)雅的代碼結(jié)構唱矛。

設計模式其實也是一門藝術。設計模式源于生活俊戳,不要為了套用設計模式而使用設計模式揖赴。

img

喜歡的小伙伴,歡迎點贊收藏關注

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抑胎,一起剝皮案震驚了整個濱河市燥滑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阿逃,老刑警劉巖铭拧,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異恃锉,居然都是意外死亡搀菩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門破托,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肪跋,“玉大人,你說我怎么就攤上這事土砂≈菁龋” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵萝映,是天一觀的道長吴叶。 經(jīng)常有香客問我,道長序臂,這世上最難降的妖魔是什么蚌卤? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上逊彭,老公的妹妹穿的比我還像新娘咸灿。我一直安慰自己,他們只是感情好诫龙,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布析显。 她就那樣靜靜地躺著鲫咽,像睡著了一般签赃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上分尸,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天锦聊,我揣著相機與錄音,去河邊找鬼箩绍。 笑死孔庭,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的材蛛。 我是一名探鬼主播圆到,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼卑吭!你這毒婦竟也來了芽淡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤豆赏,失蹤者是張志新(化名)和其女友劉穎挣菲,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掷邦,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡白胀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了抚岗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片或杠。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宣蔚,靈堂內(nèi)的尸體忽然破棺而出向抢,到底是詐尸還是另有隱情,我是刑警寧澤件已,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布笋额,位于F島的核電站,受9級特大地震影響篷扩,放射性物質(zhì)發(fā)生泄漏兄猩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望枢冤。 院中可真熱鬧鸠姨,春花似錦、人聲如沸淹真。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽核蘸。三九已至巍糯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間客扎,已是汗流浹背祟峦。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徙鱼,地道東北人宅楞。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像袱吆,于是被迫代替她去往敵國和親厌衙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容