大話設計模式讀書筆記(第1章)
阿新 • • 發佈:2020-07-22
人物:小菜,大鳥
事件:小菜去求職,求職題目是用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); }
大鳥:就這樣,只要輸入運算子號,工廠就能例項化物件,通過多型,返回父類的方式得到結果
大鳥:程式設計是一門技術,更加是一門藝術,要考慮讓程式碼更加簡練,更容易維護,容易拓展和複用。