如何去除代碼中的多次if而引發的一連串面試問題

面試官:有如下的代碼,如何去除代碼中的if?

    public int calculate(int a, int b, String action) {
        if ("ADD".equals(action)) {
            return a + b;
        }

        if ("MUL".equals(action)) {
            return a * b;
        }

        if ("DIV".equals(action)) {
            return a / b;
        }

        if ("SUB".equals(action)) {
            return a - b;
        }

        throw new RuntimeException("action unknown");
    }

小白:巴拉巴拉寫了一堆代碼,代碼如下:

public interface Calculator {
    int execute(int a, int b);
}

public class Addition implements Calculator {
    @Override
    public int execute(int a, int b) {
        return a + b;
    }
}

public class Subtraction implements Calculator {
    @Override
    public int execute(int a, int b) {
        return a - b;
    }
}

public class Multiplication implements Calculator {
    @Override
    public int execute(int a, int b) {
        return a * b;
    }
}

public class Division implements Calculator {
    @Override
    public int execute(int a, int b) {
        return a / b;
    }
}

public class CalculatorHolder {
    private static Map<String, Calculator> calculatorMap = new HashMap<>();

    static {
        calculatorMap.put("ADD", new Addition());
        calculatorMap.put("SUB", new Subtraction());
        calculatorMap.put("MUL", new Multiplication());
        calculatorMap.put("DIV", new Division());
    }

    publicstatic Calculator getCalculator(String action) {
        return calculatorMap.get(action);
    }
}

# 使用
CalculatorHolder.getCalculator( "ADD").execute(1,2);
CalculatorHolder.getCalculator( "MUL").execute(1,2);

面試官:你的這種解決方案,有人說使用了工廠模式,你覺得是嗎? 

小白:不是,真正的工廠模式有兩種:工廠方法和抽象工廠。工廠方法使用繼承,首先定義一個抽象父類工廠,然後定義子類工廠,把工廠要創建的對象委託給子工廠類,子工廠類實現父工廠類中要創建對象的方法。抽象工廠使用對象組合,首先定義一個工廠接口,然後定義工廠接口實現類,在實現類中實現接口的方法來創建對象,最後使用組合將接口實現類注入到要創建對象的地方。還有一種是簡單工廠,但它不能算是真正意義上的設計模式,就是通過ifelse方式實現的。

面試官:你覺得工廠模式有什麼優缺點? 

小白:優點就是擴展容易,也不用再修改以前的代碼,新增對象創建工廠即可,減少對象創建的依賴和強耦合。缺點就是會產生很多的工廠類或對象類,管理上會麻煩些,代碼變的更多了。

面試官:你剛剛的這種解決方案,如果對象的生命週期是由Spring容器來管理的,也就是bean的實例化是在Spring容器啓動時發生的,如何將Addition這種對象實例注入到calculatorMap中? 

小白:有兩種方式,一種是將calculatorMap中的key和value(bean的id)配置到Spring的xml聲明文件中,另一種是通過編碼的方式,自定義一個類實現Spring的ApplicationContextAware接口和InitializingBean接口,在類中聲明屬性一個ApplicationContext,通過setApplicationContext將這個屬性指向Spring容器中的應用上下文ApplicationContext對象,然後實現afterPropertiesSet方法,通過applicationContext.getBeansOfType(Calculator.class)獲取Calculator接口的所有實現類,結果是一個Map,key是bean的name,value是bean的實例對象,然後遍歷這個Map,將元素注入到calculatorMap中。

面試官:你還能用其它設計模式去除if嗎? 

小白:策略模式。

面試官:說一下你理解的策略模式? 

小白:在運行時,通過不同的策略(創建或引用不同的對象)改變類的行爲,其實就是根據實現類的不同,動態的調用不同類的相同方法。

面試官:能不能寫一下具體代碼? 

小白:一頓操作,霹靂吧啦,代碼如下:

public class CalculatorHolder {

    private Calculator calculator;

    public CalculatorHolder(Calculator calculator) {
        this.calculator = calculator;
    }

    public int execute(int a, int b){
        return calculator.execute(a, b);
    }
}

CalculatorHolder ch = new CalculatorHolder(new Addition());
ch.execute(1, 2);

ch = new CalculatorHolder(new Subtraction());
ch.execute(1, 2);

面試官:策略模式有什麼優缺點?

小白:優點就是可以解決像ifelse這樣的不好維護的代碼,做到對擴展開放、對修改關閉。缺點就是也會產生很多像Addition等這樣的代碼,導致類膨脹。

面試官:你剛剛說到開閉原則,設計模式還有其它什麼原則? 

小白:里氏替換原則,任何基類可以出現的地方,子類一定可以出現;依賴倒置原則,面向接口編程,依賴於抽象而不依賴於具體實現;單一職責原則,一個類應該只負責一項職責,做到職責單一;迪米特法則,一個對象應該對其它對象保持最少的瞭解;接口隔離原則,類和類之間應該建立在最小接口上。

面試官:解釋一下接口隔離原則的內容? 

小白:......巴拉巴拉......


推薦閱讀:

String引發的提問,我差點跪了

就寫了一行代碼,被問了這麼多問題

面試官:JVM對鎖進行了優化,都優化了啥?

synchronized連環問

高併發編程-ExecutorCompletionService深入解析

如有收穫,請點擊底部右下角"在看",謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章