一、前言
在實際的開發程序中,我們經常會遇到這樣的情況,在進行除錯分析問題的時候,經常需要記錄日志資訊,這時可以采用輸出到控制臺,
因此,我們通常會定義一個日志類,來實作輸出日志,
定義一個生成驗證的邏輯處理方法,
public class Logger
{
public void AddLogger()
{
Console.WriteLine("日志新增成功!");
}
}
然后在控制臺中輸出結果,
static void Main(string[] args)
{
Logger logger = new Logger();
logger.AddLogger();
Console.Read();
}

看著實作的結果,我們以為完成任務了,當其實這才是剛剛開始,
二、開始
相信大家在開發中,都會遇到這種情況,有時需要控制臺輸出,但也有可能要你輸出到文本,資料庫或者遠程服務器等等,這些都是有可能,因此最初采用直接輸出到控制臺已經不能滿足條件了,所以我們需要將上述代碼進行重寫改造,實作不同的輸出方式,
2.1 第一種方式
2.1.1 控制臺方式
用到控制臺方式輸出的時候:
/// <summary>
/// 控制臺輸出
/// </summary>
public class ConsoleLogger
{
public void AddLogger()
{
Console.WriteLine("控制臺輸出:日志新增成功!");
}
}
定義一個獲取輸出日志的處理邏輯類,因此我們需要定義ConsoleLogger類并初始化
/// <summary>
/// 定義一個輸出日志的統一類
/// </summary>
public class LoggerServer
{
private readonly ConsoleLogger consoleLogger = new ConsoleLogger();//添加一個私有變數的物件 個私有變數的數字物件
public void AddLogger()
{
consoleLogger.AddLogger();
}
}
控制臺輸出結果:
static void Main(string[] args)
{
LoggerServer loggerServer = new LoggerServer();
loggerServer.AddLogger();
Console.Read();
}
控制臺輸出:日志新增成功!
2.1.2 文本輸出
當用到文本輸出日志的時候,我們再次定義一個生成文本日志的方式
/// <summary>
/// 文本輸出
/// </summary>
public class FileLogger
{
public void AddLogger()
{
Console.WriteLine("文本輸出:日志新增成功!");
}
}
然后再次定義一個獲取驗證的處理邏輯類,因此我們需要定義ImageVerification類并初始化
/// <summary>
/// 定義一個輸出日志的統一類
/// </summary>
public class LoggerServer
{
private readonly FileLogger fileLogger = new FileLogger();//添加一個私有變數的物件
public void AddLogger()
{
fileLogger.AddLogger();
}
}
最后輸出結果:
文本輸出:日志新增成功!
通過以上的方式,我們實作了不同方式輸出不同日志方式,但是仔細觀察可以發現,這種方式不是一種良好的軟體設計方式,
所以可能有人會改成下面第二種方式,以介面來實作,
2.2 第二種方式
定義一個ILogger介面并宣告一個AddLogger方法
public interface ILogger
{
void AddLogger();
}
在控制臺輸出方式中ConsoleLogger類,實作ILogger介面
/// <summary>
/// 控制臺輸出
/// </summary>
public class ConsoleLogger : ILogger
{
public void AddLogger()
{
Console.WriteLine("控制臺輸出:日志新增成功!");
}
}
在文本輸出日志方式FileLogger類中,實作ILogger介面
/// <summary>
/// 文本輸出
/// </summary>
public class FileLogger : ILogger
{
public void AddLogger()
{
Console.WriteLine("文本輸出:日志新增成功!");
}
}
定義一個統一的輸出日志類LoggerServer類
/// <summary>
/// 定義一個獲取驗證的統一類
/// </summary>
public class VerificationServer
{
private readonly ConsoleLogger consoleLogger = new ConsoleLogger();//添加一個私有變數的物件
//private readonly FileLogger fileLogger = new FileLogger();//添加一個私有變數的物件
public void AddLogger()
{
_logger.AddLogger();
}
}
最后,控制臺呼叫輸出:
static void Main(string[] args)
{
LoggerServer loggerServer = new LoggerServer();
loggerServer.AddLogger();
Console.Read();
}
控制臺輸出:日志新增成功!
雖然第二種方式中,采用了介面來實作,降低耦合,但還是沒有達到我們想要的效果,因此以上的兩種方式,都不是很好的軟體設計方式,
代碼可拓展性比較差,以及組件之間存在高度耦合,違反了開放關閉原則,在設計的時候,也應當考慮對擴展開放,對修改關閉,
2.3 思考
既然要遵循開放關閉原則,那上面的寫法,選擇采用控制臺日志輸出方式所需要的ConsoleLogger創建和依賴都是在統一的日志類LoggerServer內部進行的,既然要使內部不直接存在系結依賴,那有沒有什么方式從外部傳遞的方式給LoggerServer類內部參考使用呢?
三、引入
3.1 依賴注入
依賴注入 : 它提供一種機制,將需要依賴物件的參考傳遞給被依賴物件,
下面我們先看看具體的幾種注入方式,再做小結說明,
3.1.1 建構式注入
在LoggerServer類中,定義一個私有變數_logger, 然后通過建構式的方式傳遞依賴
public class LoggerServer
{
private ILogger _logger; //1. 定義私有變數
//2.建構式
public LoggerServer(ILogger logger)
{
//3.注入 ,傳遞依賴
this._logger = logger;
}
public void AddLogger()
{
_logger.AddLogger();
}
}
通過控制臺程式呼叫,先在外部創建依賴物件,而后通過構造的方式注入依賴
static void Main(string[] args)
{
#region 建構式注入
// 注入控制臺輸出方式
// 外部創建依賴的物件 -> ConsoleLogger
ConsoleLogger console = new ConsoleLogger();
// 通過建構式注入 -> LoggerServer
LoggerServer loggerServer1 = new LoggerServer(console);
loggerServer1.AddLogger();
// 注入 檔案輸出方式
FileLogger file = new FileLogger();
// 通過建構式注入 -> LoggerServer
LoggerServer loggerServer2 = new LoggerServer(file);
loggerServer2.AddLogger();
#endregion
Console.Read();
}
輸出:
控制臺輸出:日志新增成功!
文本輸出:日志新增成功!
顯然的發現,通過這種建構式注入的方式,在外部定義依賴,降低內部的耦合度,同時也增加了擴展性,只需從外部修改依賴,就可以實作不同的驗證結果,
3.1.2 屬性注入
即通過定義一個屬性來傳遞依賴
/// <summary>
/// 定義一個輸出日志的統一類
/// </summary>
public class LoggerServer
{
//1.定義一個屬性,可接收外部賦值依賴
public ILogger _logger { get; set; }
public void AddLogger()
{
_logger.AddLogger();
}
}
通過控制臺,定義不同的方式,通過不同依賴賦值,實作不同的驗證結果:
static void Main(string[] args)
{
#region 屬性注入
// 注入 控制臺輸出方式
//外部創建依賴的物件 -> ConsoleLogger
ConsoleLogger console = new ConsoleLogger();
LoggerServer loggerServer1 = new LoggerServer();
//給內部的屬性賦值
loggerServer1._logger = console;
loggerServer1.AddLogger();
// 注入 檔案輸出方式
//外部創建依賴的物件 -> FileLogger
FileLogger file = new FileLogger();
LoggerServer loggerServer2 = new LoggerServer();
//給內部的屬性賦值
loggerServer2._logger = file;
loggerServer2.AddLogger();
#endregion
Console.Read();
}
輸出
控制臺輸出:日志新增成功!
文本輸出:日志新增成功!
3.1.3 介面注入
先定義一個介面,包含一個設定依賴的方法,
public interface IDependent
{
void SetDepend(ILogger logger);//設定依賴項
}
這個與之前的注入方式不一樣,而是通過在類中繼承并實作這個介面,
public class VerificationServer : IDependent
{
private ILogger _logger;
// 繼承介面,并實作依賴項方法,注入依賴
public void SetDepend(ILogger logger)
{
_logger = logger;
}
public void AddLogger()
{
_logger.AddLogger();
}
}
通過呼叫,直接通過依賴項方法,傳遞依賴
static void Main(string[] args)
{
#region 介面注入
// 注入 控制臺輸出方式
//外部創建依賴的物件 -> ConsoleLogger
ConsoleLogger console = new ConsoleLogger();
LoggerServer loggerServer1 = new LoggerServer();
//給內部賦值,通過介面的方式傳遞
loggerServer1.SetDepend(console);
loggerServer1.AddLogger();
//注入 檔案輸出方式
//外部創建依賴的物件 -> FileLogger
FileLogger file = new FileLogger();
LoggerServer loggerServer2 = new LoggerServer();
//給內部賦值,通過介面的方式傳遞
loggerServer2.SetDepend(file);
loggerServer2.AddLogger();
#endregion
Console.Read();
}
輸出
控制臺輸出:日志新增成功!
文本輸出:日志新增成功!
3.1.4 小結
依賴注入(DI—Dependency Injection)
它提供一種機制,將需要依賴物件的參考傳遞給被依賴物件通過DI,我們可以在LoggerServer類在外部ConsoleLogger物件的參考傳遞給LoggerServer類物件, 注入某個物件所需要的外部資源(包括物件、資源、常量資料)
依賴注入把物件的創造交給外部去管理,很好的解決了代碼緊耦合的問題,是一種讓代碼實作松耦合的機制,
松耦合讓代碼更具靈活性,能更好地應對需求變動,以及方便單元測驗,

3.2 IOC
控制反轉(Inversion of Control,縮寫為IoC),在面向物件編程中,是一種軟體設計模式,教我們如何設計出更優良,更具有松耦合的程式,
在上文的例子中,我們發現如果在獲取物件的程序中靠類內部主動創建依賴物件,則會導致代碼直接高度耦合并且期存在難以維護這種隱患,所以為了避免這種問題,我們采用了由外部提供依賴物件,內部物件類被創建的時候,將其所依賴的物件參考傳遞給它,實作了依賴被注入到物件中去,
通俗的說明:
在類A中用到了類B的物件時候,一般情況下,需要在A的代碼中顯式的new一個B的物件,這種方式都是通過我們自己主動創建出來的,創建合作物件的主動權在自己手上,自己需要哪個物件,就主動去創建,創建物件的主動權和創建時機是由自己把控的,而這樣就會使得物件間的耦合度高了,A物件需要使用物件B來共同完成一件事,A要使用B,那么A就對B產生了依賴,也就是A和B之間存在一種耦合關系,并且是緊密耦合在一起,
public class A { private B b = new B();//主動的new一個B的物件,主動創建出來 public void Get() { B.Create(); } }采用依賴注入技術之后,A的代碼只需要定義一個私有的B物件,不需要直接new來獲得這個物件,而是通過相關的容器控制程式來將B物件在外部new出來并注入到A類里的參考中,現在創建物件而是有第三方控制創建,你要什么物件,它就給你什么物件,依賴關系就變了,原先的依賴關系就沒了,A和B之間耦合度也就減少了,
public class A { private B b;//外部new出來, 注入到參考中 public void Get() { B.Create(); } }
3.3 關系
控制反轉(IoC) 是一種軟體設計的模式,指導我們設計出更優良,更具有松耦合的程式,
而具體的實作方式有依賴注入和依賴查找,
在這一篇主要說的是常用的依賴注入方式,
你在實際開發中,可能還會聽到另一名詞叫 IoC容器,這其實是一個依賴注入的框架,
用來映射依賴,管理物件創建和生存周期, (在后續篇章會具體說明)
四、思考
說到依賴,就想到依賴注入和工廠模式這兩者的區別?
這是網上有一個對比例子:
| 工廠設計模式 | 依賴注入 | |
|---|---|---|
| 物件創建 | 它用于創建物件,我們有單獨的Factory類,其中包含創建邏輯, | 它負責創建和注入物件, |
| 物件的狀態 | 它負責創建有狀態物件, | 負責創建無狀態物件 |
| 運行時/編譯時間 | 在編譯時創建物件 | 在運行時配置物件 |
| 代碼變更 | 如果業務需求發生變化,則可能會更改物件創建邏輯, | 無需更改代碼 |
| 機制 | 類依賴于工廠方法,而工廠方法又依賴于具體類 | 父物件和所有從屬物件可以在單個位置創建 |
好啦,這篇文章就先講述到這里吧,在后續篇章中會對常用的IOC容器進行使用說明,希望對大家有所幫助,
如果有不對的或不理解的地方,希望大家可以多多指正,提出問題,一起討論,不斷學習,共同進步,??
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/270447.html
標籤:.NET技术

