从循环引用谈依赖倒置原则
?
在業(yè)務開發(fā)中,通常會按照業(yè)務或者邏輯將項目分成好幾個工程文件以方便重用和模塊化,有時候我們分開的兩個項目可能存在相互引用的情況,舉個例子,比如有兩個系統(tǒng),訂單系統(tǒng)和產(chǎn)品系統(tǒng),訂單系統(tǒng)需要從產(chǎn)品系統(tǒng)中了解當前產(chǎn)品是否有剩余。產(chǎn)品系統(tǒng)需要從訂單系統(tǒng)中了解產(chǎn)品的銷售情況,這時候就存在相互引用的情況。
循環(huán)引用在Visual Studio中是編譯不通過的。出現(xiàn)循環(huán)引用很可能是設(shè)計上抽象不夠?qū)е碌?#xff0c;根據(jù)設(shè)計模式的依賴倒置-高層模塊不應該依賴于低層模塊。二者都應該依賴于抽象,抽象不應該依賴于細節(jié),細節(jié)應該依賴于抽象這一原則,可以來解決循環(huán)引用。
在一些項目中,使用一些依賴注入的框架如SPRING.net,CASTLE可以在一定程度上避免循環(huán)引用。 Class A中用到了Class B的對象b,一般情況下,需要在A的代碼中顯式的new一個B的對象。采用依賴注入技術(shù)之后,A的代碼只需要定義一個私有的B對象,不需要直接new來獲得這個對象,而是通過相關(guān)的容器控制程序來將B對象在外部new出來并注入到A類里的引用中。而具體獲取的方法、對象被獲取時的狀態(tài)由配置文件(如XML)來指定。
但有時候,項目中一些小的功能點如果使用這些框架會顯得“過重”,并且解決功能點之間的循環(huán)引用也不太復雜,簡言之就是抽象出接口。下面就演示一下如何解決項目間的循環(huán)引用。
為了演示,首先新建Product 和Order兩個類庫,Product類庫中內(nèi)容如下:
/// <summary> /// Product實體類 /// </summary> public class Product {public int ProductId { get; set; }public int OrderId { get; set; }public string ProductName { get; set; }public override string ToString(){return String.Format("the product of [{0}] infomations as followings: \r\n OrderId: {1} \r\n ProductName: {2}",OrderId, ProductId, ProductName);} } public class ProductService {/// <summary>/// 根據(jù)OrderID獲取Product/// </summary>/// <param name="orderId"></param>/// <returns></returns>public Product GetProductsByOrderId(int orderId){Product product = new Product(){ProductId = 1,OrderId = orderId,ProductName = "test product"};return product;} }里面有一個Product實體類,然后一個ProductService類用來提供服務,其中有一個名為GetProductsByOrderId的方法可以通過OrderId查詢產(chǎn)品信息。
Order類庫類似,提供了一個Order實體類和OrderService類來提供服務,其中有一個名為GetOrdersByProductId的方法可以通過ProductId查詢訂單信息。
/// <summary> /// Order實體類 /// </summary> public class Order {public int OrderId { get; set; }public int ProductId { get; set; }public string OrderName { get; set; }public override string ToString(){return String.Format("the order of [{0}] information are as followings: \r\n ProductId: {1} \r\n OrderName: {2}",OrderId, ProductId, OrderName);} } public class OrderService {public Order GetOrdersByProductId(int productId){Order order = new Order(){OrderId = 1,ProductId = productId,OrderName = "test order"};return order;} }現(xiàn)在, 假設(shè)我們在Product類中需要調(diào)用Order類中的GetOrdersByProductId方法查詢訂單,那么需要引用Order工程文件,因為實體和方法都在Order類庫中,這時Product對Order類庫產(chǎn)生了依賴,假設(shè)與此同時,Order類中也需要調(diào)用Product類中的GetProductsByOrderId方法來查詢產(chǎn)品,這樣Order類庫就對Product產(chǎn)生了依賴。就出現(xiàn)了循環(huán)引用的情況。
在這種情況下,我們新建一個Shared類庫,將Order和Product中需要對外依賴的部分抽象成接口IOrder和IProduct放到這個第三方庫中,并定義一些需要交換數(shù)據(jù)的實體類OrderModel和ProductModel。
public interface IProduct {ProductModel GetProductsByOrderId(int orderId); } public interface IOrder {OrderModel GetOrdersByProductId(int productId); }然后Order和Product項目引用Shared類庫,并實現(xiàn)定義好的接口,現(xiàn)在Product類庫中的ProductService方法實現(xiàn)IProduct 接口,變?yōu)榱?#xff1a;
public class ProductService:IProduct {/// <summary>/// 接口方法,根據(jù)OrderId獲取產(chǎn)品信息/// </summary>/// <param name="orderId"></param>/// <returns></returns>public ProductModel GetProductsByOrderId(int orderId){ProductModel result;Product product = GetProduct(orderId);result= new ProductModel {OrderId = product.OrderId, ProductName = product.ProductName, ProductId = product.ProductId };return result;}/// <summary>/// 根據(jù)OrderID獲取Product/// </summary>/// <param name="orderId"></param>/// <returns></returns>private Product GetProduct(int orderId){Product product = new Product(){ProductId = 1,OrderId = orderId,ProductName = "test product"};return product;} }在實現(xiàn)的接口方法內(nèi)部,我們可以調(diào)用其它的方法,只需要將返回結(jié)果Product轉(zhuǎn)換為接口中定義的返回類型ProductModel即可,因為有些時候,我們可能不需要對外提供那么多的信息。只需要提供指定了的信息即可。然后在Product 類庫中我們提供一個產(chǎn)生ProductService實體的類ProductSharedService,中間只有一個Create方法,該方法返回ProductService實體。
public static class ProductSharedService {public static ProductService Create(){return new ProductService();} }我們還需要在Shared類庫中提供動態(tài)加載程序集,反射調(diào)用ProductSharedService的Create方法的通用方法。于是新建類SharedInterfaceProxy,通過構(gòu)造函數(shù)傳入程序集名稱,以及創(chuàng)建實體類的類名稱。
public class SharedInterfaceProxy {private string _assemblyName;private string _className;private const string _methodName = "Create";private MethodInfo _proxcy;/// <summary>/// 構(gòu)造函數(shù)/// </summary>/// <param name="assemblyName">程序集名稱</param>/// <param name="className">程序集中產(chǎn)生實體類的類名稱,該類必須存在一個名為Create的方法,這里是約定</param>public SharedInterfaceProxy(string assemblyName, string className){this._assemblyName = assemblyName;this._className = className;Init();}private void Init(){Type type;try{type = GetAssemblyType();if (type != null){_proxcy = type.GetMethod(_methodName);}}catch (Exception){throw;}}private Type GetAssemblyType(){Type result;Assembly assem;result = null;try{assem = AppDomain.CurrentDomain.Load(_assemblyName);if (assem != null){result = assem.GetType(_className);}}catch (Exception ex){Debug.WriteLine(ex.Message);}return result;}public object GetInstance(){object result;if (_proxcy == null){return null;}result = null;try{result = _proxcy.Invoke(null, null);}catch (Exception ex){Debug.WriteLine(ex.Message);}return result;} }方法中我們約定了傳進去的className這個類必須存在一個Create方法,使用反射調(diào)用該方法就可以產(chǎn)生創(chuàng)建一個服務實體。
現(xiàn)在,假設(shè)我們需要在Order類庫中調(diào)用Product的GetProductsByOrderId 方法,現(xiàn)在我們可以通過 實例化SharedInterfaceProxy類,然后通過接口實現(xiàn)調(diào)用。
public static ProductModel GetProductByOrderId(int orderId) {ProductModel result;result = null;SharedInterfaceProxy shareProxy = new SharedInterfaceProxy("Product", "Product.ProductSharedService");object product=shareProxy.GetInstance();if (product != null){IProduct pro = product as IProduct;if (pro != null){result = pro.GetProductsByOrderId(orderId);}}return result; }以上就是通過使用反射和接口實現(xiàn)了循環(huán)引用工程的解耦和,其基本原理就是設(shè)計模式中的依賴倒置原理,即高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節(jié);細節(jié)應該依賴抽象。這里在本文之前Product依賴Order高層模塊,Order也依賴Product高層模塊,改造之后,兩者都依賴于Shared中的接口這一抽象。
本來想畫幾個UML圖的,這樣就一目了然,可惜不太會,后面會補上。
希望本文對您了解如何簡單的解決循環(huán)引用以及對設(shè)計模式中的依賴倒置有所理解。
總結(jié)
以上是生活随笔為你收集整理的从循环引用谈依赖倒置原则的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 买部iPhone 14要花你多少年薪?各
- 下一篇: 暂时赢了:英国法庭驳回针对 Meta 的