1. 程式人生 > 實用技巧 >大話設計模式讀書筆記(第1章)

大話設計模式讀書筆記(第1章)

人物:小菜,大鳥

事件:小菜去求職,求職題目是用Java等任意一種面嚮物件語言,實現一個計算機控制檯程式,要求輸入兩個數和運算子,得到結果


小菜的初次實現

@Slf4j
public class Program {
    public static void main(String[] args) {
        log.info("每輸入一個數字或者運算子號請敲回車");
        Scanner scanner = new Scanner(System.in);
        BigDecimal A = scanner.nextBigDecimal();
        String calculateChar 
= scanner.next(); BigDecimal numberB = scanner.nextBigDecimal(); if (calculateChar.equalsIgnoreCase("-")) { log.info(A.subtract(numberB).toString()); } if (calculateChar.equalsIgnoreCase("+")) { log.info(A.add(numberB).toString()); }
if (calculateChar.equalsIgnoreCase("*")) { log.info(A.multiply(numberB).toString()); } if (calculateChar.equalsIgnoreCase("/")) { log.info(A.divide(numberB).toString()); } } }

大鳥指出了程式碼缺點:

1.第一個數字的命名不規範

2.if...else...語句,會無用判斷三次

3.如果numberB輸入的是0,則程式碼會報錯

小菜的再次嘗試

@Slf4j
public class Program {
    public static void main(String[] args) {
        log.info("每輸入一個數字或者運算子號請敲回車");
        Scanner scanner = new Scanner(System.in);
        BigDecimal numberA = scanner.nextBigDecimal();
        String calculateChar = scanner.next();
        BigDecimal numberB = scanner.nextBigDecimal();
        String result = "";
        try {
            switch (calculateChar) {
                case "+":
                    result = numberA.add(numberB).toString();
                case "-":
                    result = numberA.subtract(numberB).toString();
                case "*":
                    result = numberA.multiply(numberB).toString();
                case "/":
                    if (BigDecimal.ZERO.equals(numberB)) {
                        log.info("除數不能是0");
                    } else {
                        result = numberA.divide(numberB).toString();
                    }
                    break;
            }
            log.info("最後計算出的數字是:{}", result);
        } catch (Exception e) {
            log.info("輸入有誤,請重新輸入");
        }
    }
}

大鳥評價:就目前來說,實現功能是沒有問題了,但還需確認這樣設計是否符合出題人的題意

面向物件程式設計

作者以曹操寫詩,刻板,改詩,刻板,改詩,再刻板舉例,改一個字,就需要全部重新刻板,而活字印刷,則改一字就重印一字即可:

1.改字,即只需重新印刷要改之字 --可維護

2.活體印刷這次用了,下次還可以繼續使用 --可複用

3.一篇文章若要加字,則再刻一字即可 --可拓展

4.可改變排版格式,橫排豎排都可 --靈活性好

小菜嘗試將程式碼複用(業務的封裝)

@Slf4j
public class Program {
    public static void main(String[] args) {
        try {
            log.info("每輸入一個數字或者運算子號請敲回車");
            Scanner scanner = new Scanner(System.in);
            BigDecimal numberA = scanner.nextBigDecimal();
            String calculateChar = scanner.next();
            BigDecimal numberB = scanner.nextBigDecimal();
            String result = getResult(numberA, numberB, calculateChar);
            if (StringUtils.isEmpty(result)) {
                log.info("運算子請輸入 + - * 或 /");
            } else {
                log.info("最後計算出的數字是:{}", result);
            }
        } catch (Exception e) {
            log.info("輸入有誤,請重新輸入");
        }
    }

    public static String getResult(BigDecimal numberA, BigDecimal numberB, String calculateChar) {
        switch (calculateChar) {
            case "+":
                return numberA.add(numberB).toString();
            case "-":
                return numberA.subtract(numberB).toString();
            case "*":
                return numberA.multiply(numberB).toString();
            case "/":
                if (BigDecimal.ZERO.equals(numberB)) {
                    log.info("除數不能是0");
                } else {
                    return numberA.divide(numberB).toString();
                }
                break;
        }
        return null;
    }
}  

大鳥:

業務和介面已經分離,現在已經用了面向物件三大特性之一的封裝了,那麼另外兩個繼承和多型改怎麼運用到這個例子中呢?

現在要求你加一個開根號的運算子

小菜:

那直接在switch上加一個開根號就行(其實以前我也是,有什麼需要就加什麼,完全沒有考慮過設計模式的問題,使得程式碼很長又不好維護)

大鳥:

原來getResult()介面的內容被暴露了出來,增加了風險,可能不小心修改錯誤會導致一大片邏輯錯誤

小菜嘗試用繼承方式實現

@Data
public class Operation {
    private BigDecimal numberA = BigDecimal.ZERO;
    private BigDecimal numberB = BigDecimal.ZERO;

    public String getResult() {
        BigDecimal result = BigDecimal.ZERO;
        return result.toString();
    }
}

將加減乘除分出來封裝好(加法如下),依次繼承,供以呼叫,如果要增加開根號,則再寫一個子類即可  

public class OperationAdd extends Operation {
    @Override
    public String getResult() {
        BigDecimal result = getNumberA().add(getNumberB());
        return result.toString();
    }
}

大鳥:思路不錯,接下來就是考慮如何例項化物件的問題了,下面是簡單工廠模式,可以參考解決這個問題  

簡單工廠類:

public class OperationFactory {
    public static Operation createOperate(String operate) {
        Operation oper = null;
        switch (operate) {
            case "+":
                oper = new OperationAdd();
            case "-":
                //oper = new OperationSub();
            case "*":
                //oper = new OperationMul();
            case "/":
                //oper = new OperationDiv();
                break;
        }
        return oper;
    }
}

客戶端程式碼:

public static void main(String[] args) {
        Operation oper;
        oper = OperationFactory.createOperate("+");
        oper.setNumberA(new BigDecimal("1"));
        oper.setNumberB(new BigDecimal("2"));
        String  result = oper.getResult();
        log.info("運算結果為:{}", result);
}

大鳥:就這樣,只要輸入運算子號,工廠就能例項化物件,通過多型,返回父類的方式得到結果  

大鳥:程式設計是一門技術,更加是一門藝術,要考慮讓程式碼更加簡練,更容易維護,容易拓展和複用。