Clean Code_Note9_Replace Switch Statement With Subclasses

 
switch case 應該在寫程式時很常用到
那往往也會讓程式碼看起來比較有結構性但也可能因為太長導致不好維護,甚至很多重複性值的程式。


以下是一個案例

 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
namespace CleanUpConditionals
{
    public class ReplaceSwitchStatementWithSubclasses
    {
        public ReplaceSwitchStatementWithSubclasses()
        {
            var lowLevelManager = new Manager(Manager.LOW_LEVEL, 30000);
            decimal salary = lowLevelManager.GetMonthlySalary(0);
        }

        class Manager
        {
            public const int LOW_LEVEL = 0;
            public const int MIDDLE_LEVEL = 1;
            public const int TOP_LEVEL = 2;

            private int _managerLevel;
            private decimal _baseSalary;

            public Manager(int level, decimal baseSalary)
            {
                _managerLevel = level;
                _baseSalary = baseSalary;
            }

            public decimal GetMonthlySalary(int numberOfPeopleInDepartment)
            {
                switch (_managerLevel)
                {
                    case LOW_LEVEL:
                        return _baseSalary;
                    case MIDDLE_LEVEL:
                        return _baseSalary * 1.25M ;
                    case TOP_LEVEL:
                        return (_baseSalary * 1.25M) + (decimal)(numberOfPeopleInDepartment * 0.05);
                    default:
                        return 0;
                }
            }
        }
    }
}


想像一下今天又再擴充另一個method叫做GetManagerTitle()的method
然後一樣會因為managerLevel有差異又要再次重複寫switch case


 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
namespace CleanUpConditionals
{
    public class ReplaceSwitchStatementWithSubclasses
    {
        public ReplaceSwitchStatementWithSubclasses()
        {
            var lowLevelManager = new Manager(Manager.LOW_LEVEL, 30000);
            decimal salary = lowLevelManager.GetMonthlySalary(0);
        }

        class Manager
        {
            public const int LOW_LEVEL = 0;
            public const int MIDDLE_LEVEL = 1;
            public const int TOP_LEVEL = 2;

            private int _managerLevel;
            private decimal _baseSalary;

            public Manager(int level, decimal baseSalary)
            {
                _managerLevel = level;
                _baseSalary = baseSalary;
            }

            public decimal GetMonthlySalary(int numberOfPeopleInDepartment)
            {
                switch (_managerLevel)
                {
                    case LOW_LEVEL:
                        return _baseSalary;
                    case MIDDLE_LEVEL:
                        return _baseSalary * 1.25M ;
                    case TOP_LEVEL:
                        return (_baseSalary * 1.25M) + (decimal)(numberOfPeopleInDepartment * 0.05);
                    default:
                        return 0;
                }
            }

            public string GetManagerTitle()
            {
                switch (_managerLevel)
                {
                    case LOW_LEVEL:
                        return "Low Level Manager";
                    case MIDDLE_LEVEL:
                        return "Middle Level Manager";
                    case TOP_LEVEL:
                        return "Top Level Manager";
                    default:
                        return "";
                }
            }
        }
    }
}


switch case 瘋狂不斷重複的情況,這時可以去新建一個abstract ManagerLevel class
將分支判斷處理移至當中統一實作


簡化GetMonthlySalary之後的程式
處理流程思維:
我們可將不同case處理情境額外抽離出sub class各自都代表某種ManagerLevel
那最上層去繼承自abstract base class也就是ManagerLevel ,
ManagerLevel 會去統一實作判定是隸屬哪種階層的判斷。
接著各個sub class只要繼承後依序實作最上層定義要具體實作的method即可。
換言之,每個case 的sub class只要注重要做的事情而判斷誰符合條件交給上一層處理,使關注點分離。

 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
using System;

namespace CleanUpConditionals
{
    public class ReplaceSwitchStatementWithSubclasses
    {
        public ReplaceSwitchStatementWithSubclasses()
        {
            var lowLevelManager = new Manager(ManagerLevel.LOW_LEVEL, 30000);
            decimal salary = lowLevelManager.GetMonthlySalary(0);
        }

        abstract class ManagerLevel
        {
            public const int LOW_LEVEL = 0;
            public const int MIDDLE_LEVEL = 1;
            public const int TOP_LEVEL = 2;

            public static ManagerLevel GetManagerLevel(int managerLevel)
            {
                switch (managerLevel)
                {
                    case LOW_LEVEL:
                        return new LowManagerLevel();
                    case MIDDLE_LEVEL:
                        return new MiddleManagerLevel();
                    case TOP_LEVEL:
                        return new TopManagerLevel();
                    default:
                        throw new ArgumentException("Level is not supported");
                }
            }
            public abstract decimal GetBaseMonthlySalary(decimal baseSalary,int numberOfPeopleInDepartment);
        }

        class LowManagerLevel : ManagerLevel
        {
            public override decimal GetBaseMonthlySalary(decimal baseSalary, int numberOfPeopleInDepartment)
            {
                return baseSalary;
            }
        }

        class MiddleManagerLevel : ManagerLevel
        {
            public override decimal GetBaseMonthlySalary(decimal baseSalary, int numberOfPeopleInDepartment)
            {
                return baseSalary * 1.25M;
            }
        }

        class TopManagerLevel : ManagerLevel
        {
            public override decimal GetBaseMonthlySalary(decimal baseSalary, int numberOfPeopleInDepartment)
            {
                return (baseSalary * 1.25M) + (decimal)(numberOfPeopleInDepartment * 0.05);
            }
        }

        class Manager
        {
            private int _managerLevel;
            private decimal _baseSalary;
            private ManagerLevel _managerLevelObj;

            public Manager(int level, decimal baseSalary)
            {
                _managerLevel = level;
                _managerLevelObj = ManagerLevel.GetManagerLevel(level);
                _baseSalary = baseSalary;
            }

            public decimal GetMonthlySalary(int numberOfPeopleInDepartment)
            {
                return _managerLevelObj.GetBaseMonthlySalary(_baseSalary, numberOfPeopleInDepartment);
            }

            public string GetManagerTitle()
            {
                switch (_managerLevel)
                {
                    case ManagerLevel.LOW_LEVEL:
                        return "Low Level Manager";
                    case ManagerLevel.MIDDLE_LEVEL:
                        return "Middle Level Manager";
                    case ManagerLevel.TOP_LEVEL:
                        return "Top Level Manager";
                    default:
                        return "";
                }
            }
        }
    }
}


接續處理解化GetManagerTitle()
一樣就是在ManagerLevel Base Class中定義抽象方法
再去每個sub class具體實作回傳。

 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
99
using System;

namespace CleanUpConditionals
{
    public class ReplaceSwitchStatementWithSubclasses
    {
        public ReplaceSwitchStatementWithSubclasses()
        {
            var lowLevelManager = new Manager(ManagerLevel.LOW_LEVEL, 30000);
            decimal salary = lowLevelManager.GetMonthlySalary(0);
        }

        abstract class ManagerLevel
        {
            public const int LOW_LEVEL = 0;
            public const int MIDDLE_LEVEL = 1;
            public const int TOP_LEVEL = 2;

            public static ManagerLevel GetManagerLevel(int managerLevel)
            {
                switch (managerLevel)
                {
                    case LOW_LEVEL:
                        return new LowManagerLevel();
                    case MIDDLE_LEVEL:
                        return new MiddleManagerLevel();
                    case TOP_LEVEL:
                        return new TopManagerLevel();
                    default:
                        throw new ArgumentException("Level is not supported");
                }
            }
            public abstract decimal GetBaseMonthlySalary(decimal baseSalary,int numberOfPeopleInDepartment);
            public abstract string GetBaseManagerTitle();

        }

        class LowManagerLevel : ManagerLevel
        {
            public override string GetBaseManagerTitle()
            {
                return "Low Level Manager";
            }

            public override decimal GetBaseMonthlySalary(decimal baseSalary, int numberOfPeopleInDepartment)
            {
                return baseSalary;
            }
        }

        class MiddleManagerLevel : ManagerLevel
        {
            public override string GetBaseManagerTitle()
            {
                return "Middle Level Manager";
            }
            public override decimal GetBaseMonthlySalary(decimal baseSalary, int numberOfPeopleInDepartment)
            {
                return baseSalary * 1.25M;
            }
        }

        class TopManagerLevel : ManagerLevel
        {
            public override string GetBaseManagerTitle()
            {
                return "Top Level Manager";
            }
            public override decimal GetBaseMonthlySalary(decimal baseSalary, int numberOfPeopleInDepartment)
            {
                return (baseSalary * 1.25M) + (decimal)(numberOfPeopleInDepartment * 0.05);
            }
        }

        class Manager
        {
            private int _managerLevel;
            private decimal _baseSalary;
            private ManagerLevel _managerLevelObj;

            public Manager(int level, decimal baseSalary)
            {
                _managerLevel = level;
                _managerLevelObj = ManagerLevel.GetManagerLevel(level);
                _baseSalary = baseSalary;
            }

            public decimal GetMonthlySalary(int numberOfPeopleInDepartment)
            {
                return _managerLevelObj.GetBaseMonthlySalary(_baseSalary, numberOfPeopleInDepartment);
            }

            public string GetManagerTitle()
            {
                return _managerLevelObj.GetBaseManagerTitle();
            }
        }
    }
}



























留言

這個網誌中的熱門文章

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

經得起原始碼資安弱點掃描的程式設計習慣培養(三)_7.Cross Site Scripting(XSS)_Stored XSS_Reflected XSS All Clients

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題