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
    }
}










留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header