Clean Code_Note8_Eliminate Null Checks
在一段程式中當觀察到物件中充斥很多 method重覆查檢非null的判斷時
就可能是沒有從源頭下手的情境
以下為一個範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | using System; using System.Collections.Generic; namespace CleanUpConditionals { public class EliminateNullChecks { public class BattleRobot { public int Team { get; set; } public int Health { get; set; } public int XCoordinate { get; set; } public int YCoordinate { get; set; } #region Methods public void Attack(int x, int y) { //shoot missiles implementation omitted... } public List<Tuple<int, int>> GetPossibleMoves() { //some complex implementation omitted... return null; } public List<Tuple<int, int>> GetEnemyLocations() { //some complex implementation omitted... return null; } #endregion } private BattleRobot _robot; public BattleRobot Robot { get => _robot; set => _robot = value; } public bool CanStillFight() { if (_robot != null) { return true; } return false; } public void AttackPosition(int x, int y) { if (_robot != null) { _robot.Attack(x, y); } } public void UpdateStrategy() { if (_robot != null) { List<Tuple<int, int>> posssibleMoves = _robot.GetPossibleMoves(); List<Tuple<int, int>> enemyLocations = _robot.GetEnemyLocations(); foreach (var move in posssibleMoves) { DevelopStrategy(move, enemyLocations); } } } #region Other methods private void DevelopStrategy(Tuple<int, int> move, List<Tuple<int, int>> locations) { } #endregion } } |
可以觀察到在EliminateNullChecks一整個class中
CanStillFight() 、AttackPosition()、UpdateStrategy()這三個方法不斷重複在做null查檢。
此時可引入Null物件並去繼承自BattleRobot,覆寫這些方法
EliminateNullChecks原本在這三個方法中呼叫到的private欄位全部改為用屬性封裝的Robot呼叫方法,在Robot屬性中已經改為當程式被呼叫執行結果為空時,直接返回Null Object,而非Null。
改完後的程式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | using System; using System.Collections.Generic; using static CleanUpConditionals.EliminateNullChecks; namespace CleanUpConditionals { public class NullRobot : BattleRobot { public override bool CanStillFight() { return base.CanStillFight(); } public override void Attack(int x, int y) { base.Attack(x, y); } public override List<Tuple<int, int>> GetEnemyLocations() { return base.GetEnemyLocations(); } public override List<Tuple<int, int>> GetPossibleMoves() { return base.GetPossibleMoves(); } } public class EliminateNullChecks { public class BattleRobot { public int Team { get; set; } public int Health { get; set; } public int XCoordinate { get; set; } public int YCoordinate { get; set; } public virtual bool CanStillFight() { return true; } #region Methods public virtual void Attack(int x, int y) { //shoot missiles implementation omitted... } public virtual List<Tuple<int, int>> GetPossibleMoves() { //some complex implementation omitted... return null; } public virtual List<Tuple<int, int>> GetEnemyLocations() { //some complex implementation omitted... return null; } #endregion } private BattleRobot _robot; public BattleRobot Robot { get => _robot ?? new NullRobot(); set => _robot = value; } public bool CanStillFight() { return Robot.CanStillFight(); } public void AttackPosition(int x, int y) { Robot.Attack(x, y); } public void UpdateStrategy() { List<Tuple<int, int>> posssibleMoves = Robot.GetPossibleMoves(); List<Tuple<int, int>> enemyLocations = Robot.GetEnemyLocations(); foreach (var move in posssibleMoves) { DevelopStrategy(move, enemyLocations); } } #region Other methods private void DevelopStrategy(Tuple<int, int> move, List<Tuple<int, int>> locations) { } #endregion } } |
留言
張貼留言