Home [C# 筆記][IOC][DI][介面導向] 用戶管理(重構+介面)
Post
Cancel

[C# 筆記][IOC][DI][介面導向] 用戶管理(重構+介面)

[C# 筆記] 用戶管理(重構)

  • 用戶系統-用戶登入 User
  • 表單系統-顯示表單 Menu
  • 控制系統-流程控制 CMSController

User

  • isUserLogin從「成員變量」改為「成員屬性」

改成屬性有什麼好處呢?我們的 User就可以通過接口(介面)來對 IsUserLogin這個屬性來進行訪問了。

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
public class User : IUser
{
    public bool IsUserLogin { get; set; }
    public void Login()
    {
        string username;
        string password;

        //檢查帳號
        username = CmdReader("請輸入帳號");
        if (username != "riva")
        {
            Console.WriteLine("查無此人");
            return;
        }

        //檢查密碼
        password = CmdReader("請輸入密碼");
        if (password != "1234")
        {
            Console.WriteLine("密碼錯誤");
            return;
        }
        IsUserLogin = true;
    }
}

提取介面

IUser

  • 點選 User 按右鍵 > 提取介面

現在我們專案中就創建了一個 IUser

1
2
3
4
5
public interface IUser
{
    bool IsUserLogin { get; set; }
    void Login();
}

同樣的,我們給 CMSController 以及 Menu 加上他們所對應的接口(介面). (在加入接口(介面)前,先修正程式碼的錯誤)

ICMSController

  • 點選 CMSController 按右鍵 > 提取介面
1
2
3
public interface ICMSController {
    void Start(Program.User user, Program.Menu menu);
}

IMenu

  • 點選 Menu 按右鍵 > 提取介面
    1
    2
    3
    
    public interface IMenu {
      void ShowMenu();
    }
    

把User,Menu服務註冊到 CMSController

現在我們就得到了 IUserIMenuICMSController這三個接口(介面)。

現在到 CMSController,我們把 IUserIMenu這兩個服務註冊到 CMSController中:

  • 首先先創建這兩個私有服務所對應的成員變量
1
2
private readonly IUser _user;
private readonly IMenu _menu;
  • 然後把這兩個服務,通過構造方法的參數傳入進來
1
2
3
4
5
public CMSController(IUser user, IMenu menu)
{
    _user = user;
    _menu = menu;
}
  • 然後在 Start()方法中,把 usermenu這兩個參數去掉
  • 修改一下變數,因為usermenu已經從構造方法中傳遞進來了。
1
2
3
4
5
6
7
8
9
10
11
public void Start()
{
    //login
    do
    {
        _user.Login();
    } while (!_user.IsUserLogin);

    //顯示菜單
    _menu.ShowMenu();
}
  • 還有介面 ICMSController,把 usermenu這兩個參數一併刪除
1
2
3
public interface ICMSController {
    void Start();
}

使用 IoC 反轉控制容器

創建IoC+ DI注入服務

回到Main 方法,可以添加反轉控制容器了。

在添加反轉控制容器之前,先刪除所有的程式碼,然後添加IoC容器框架:

  • NuGet > Microsoft.Extensions.DependencyInjection
  • using Microsoft.Extensions.DependencyInjection;

準備工作都完成了,現在可以創建「IoC反轉控制容器」了。

  • 初始化本地變量 ServiceCollection
1
ServiceCollection collection = new ServiceCollection(); //IoC

這個 ServiceCollection就是我們的IoC

現在向這個 IoC 中分別注入:用戶User、菜單Menu、程序控制 這三個服務, 使用 AddScoped

  • 第一個用戶:使用「用戶服務介面 IUser」,實現User這個類
  • 第二個菜單:使用「菜單服務介面 IMenu」,實現Menu這個類
  • 第三個程序控制:使用「程序控制介面 ICMSController」,實現CMSController這個類

好了,服務全部註冊完了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using Microsoft.Extensions.DependencyInjection;

static void Main(string[] args)
{
    //創建「IoC反轉控制容器」
    ServiceCollection collection = new ServiceCollection(); //IoC

    //注入服務
    collection.AddScoped<IUser, User>(); //使用用戶服務介面 IUser,實現 User 這個類
    collection.AddScoped<IMenu, Menu>(); //使用菜單服務介面 IMenu,實現 Menu 這個類
    collection.AddScoped<ICMSController, CMSController>();


    Console.Read();
}

這就代表我們這三個服務的生命周期,全部都已經交給IoC 容器 collection 進行托管了。接下來我們只需要從IoC中,通過介面來撈取對應的服務就可以了,從此我們再也不需要手動創建和管理這三個服務了。

創建 build service provider

接下來創建 build service provider

1
2
//創建 build service provider
var serviceProvider = collection.BuildServiceProvider();

提取的服務 CMSController

而我們需要提取的服務,其實就是這個 CMSController,不過,提取CMSController實體必須通過介面,但是需要泛型來指定介面

1
2
//所需提取的 CMSController
var cmsController = serviceProvider.GetService<ICMSController>();//提取CMSController實體必須通過介面,但是需要泛型來指定介面

這個就是我們所需提取的 CMSController

現在重構就進入尾聲了。

啟動「整個CMS程序」

我們最後一步就是:通過這個 cmsController來啟動「整個CMS程序」

1
2
//啟動「整個CMS程序」
cmsController.Start();

而這個 start()我們不需要傳入 用戶User、菜單Menu,用戶User、菜單Menu這兩個系統也同樣已經托管給 ICO了。

IoC 會自動選擇合適的時機,在cmsController實體化的同時,在構造方法中完成這兩個服務的依賴注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void Main(string[] args)
{
    //創建「IoC反轉控制容器」
    ServiceCollection collection = new ServiceCollection(); //IoC
    //注入服務
    collection.AddScoped<IUser, User>(); //使用用戶服務介面 IUser,實現 User 這個類
    collection.AddScoped<IMenu, Menu>(); //使用菜單服務介面 IMenu,實現 Menu 這個類
    collection.AddScoped<ICMSController, CMSController>();

    //創建 build service provider
    var serviceProvider = collection.BuildServiceProvider();
    //所需提取的 CMSController
    var cmsController = serviceProvider.GetService<ICMSController>();//提取CMSController實體必須通過介面,但是需要泛型來指定介面

    //啟動「整個CMS程序」
    cmsController.Start();

    Console.Read();
}

好了,重構完成!

我們的程式碼經過「面向接口(介面導向)」的調整,實現了用戶、菜單、程序控制 這三個部分的完全解耦,任何一個組件我們都可以把它獨立出來,這樣的程式碼更加富有擴展性,以及維護性。

完整程式碼

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//用戶
public class User : IUser
{
    public bool IsUserLogin { get; set; }
    public void Login()
    {
        string username;
        string password;

        //檢查帳號
        username = CmdReader("請輸入帳號");
        if (username != "riva")
        {
            Console.WriteLine("查無此人");
            return;
        }

        //檢查密碼
        password = CmdReader("請輸入密碼");
        if (password != "1234")
        {
            Console.WriteLine("密碼錯誤");
            return;
        }
        IsUserLogin = true;
    }
}
//菜單
public class Menu : IMenu
{
    public void ShowMenu()
    {
        bool isExit = false;
        //選擇主表單
        while (!isExit)
        {
            string selection = CmdReader("主表單:1.客戶管理 2.預約管理 3.系統管理 4.退出/n請選擇:");
            switch (selection)
            {
                case "1":
                    Console.WriteLine("客戶管理");
                    break;
                case "2":
                    Console.WriteLine("預約管理");
                    break;
                case "3":
                    Console.WriteLine("系統管理");
                    break;
                case "4":
                default:
                    Console.WriteLine("退出");
                    isExit = true;
                    break;
            }
        }
    }
}
//程序控制
public class CMSController : ICMSController
{
    private readonly IUser _user;
    private readonly IMenu _menu;

    public CMSController(IUser user, IMenu menu)
    {
        _user = user;
        _menu = menu;
    }

    public void Start()
    {
        //login
        do
        {
            _user.Login();
        } while (!_user.IsUserLogin);

        //顯示菜單
        _menu.ShowMenu();
    }
}

//User介面
public interface IUser {
    bool IsUserLogin { get; set; }
    void Login();
}
//Menu介面
public interface IMenu {
    void ShowMenu();
}
//CMSController介面
public interface ICMSController {
    void Start();
}

//整個CMS程序-Main方法
namespace CMS
{
    public partial class Program
    {
        public static string CmdReader(string msg)
        {
            Console.WriteLine(msg);
            return Console.ReadLine();
        }
        static void Main(string[] args)
        {
            //創建「IoC反轉控制容器」
            ServiceCollection collection = new ServiceCollection(); //IoC
            //注入服務
            collection.AddScoped<IUser, User>(); //使用用戶服務介面 IUser,實現 User 這個類
            collection.AddScoped<IMenu, Menu>(); //使用菜單服務介面 IMenu,實現 Menu 這個類
            collection.AddScoped<ICMSController, CMSController>();

            //創建 build service provider
            var serviceProvider = collection.BuildServiceProvider();
            //所需提取的 CMSController
            var cmsController = serviceProvider.GetService<ICMSController>();//提取CMSController實體必須通過介面,但是需要泛型來指定介面

            //啟動「整個CMS程序」
            cmsController.Start();

            Console.Read();
        }
    }
}

https://www.bilibili.com/video/BV1Ss4y1B7zE?p=46

This post is licensed under CC BY 4.0 by the author.