在业务开发中,通常会按照业务或者逻辑将项目分成好几个工程文件以方便重用和模块化,有时候我们分开的两个项目可能存在相互引用的情况,举个例子,比如有两个系统,订单系统和产品系统,订单系统需要从产品系统中了解当前产品是否有剩余。产品系统需要从订单系统中了解产品的销售情况,这时候就存在相互引用的情况。

循环引用在Visual Studio中是编译不通过的。出现循环引用很可能是设计上抽象不够导致的,根据设计模式的依赖倒置-高层模块不应该依赖于低层模块。二者都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象这一原则,可以来解决循环引用。

在一些项目中,使用一些依赖注入的框架如SPRING.net,CASTLE可以在一定程度上避免循环引用。 Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式的new一个B的对象。采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。

但有时候,项目中一些小的功能点如果使用这些框架会显得“过重”,并且解决功能点之间的循环引用也不太复杂,简言之就是抽象出接口。下面就演示一下如何解决项目间的循环引用。

为了演示,首先新建Product 和Order两个类库,Product类库中内容如下:

/// <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>/// 根据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查询产品信息。

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

现在, 假设我们在Product类中需要调用Order类中的GetOrdersByProductId方法查询订单,那么需要引用Order工程文件,因为实体和方法都在Order类库中,这时Product对Order类库产生了依赖,假设与此同时,Order类中也需要调用Product类中的GetProductsByOrderId方法来查询产品,这样Order类库就对Product产生了依赖。就出现了循环引用的情况。

在这种情况下,我们新建一个Shared类库,将Order和Product中需要对外依赖的部分抽象成接口IOrder和IProduct放到这个第三方库中,并定义一些需要交换数据的实体类OrderModel和ProductModel。

public interface IProduct
{ProductModel GetProductsByOrderId(int orderId);
}
public interface IOrder
{OrderModel GetOrdersByProductId(int productId);
}

然后Order和Product项目引用Shared类库,并实现定义好的接口,现在Product类库中的ProductService方法实现IProduct 接口,变为了:

public class ProductService:IProduct
{/// <summary>/// 接口方法,根据OrderId获取产品信息/// </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>/// 根据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;}
}

在实现的接口方法内部,我们可以调用其它的方法,只需要将返回结果Product转换为接口中定义的返回类型ProductModel即可,因为有些时候,我们可能不需要对外提供那么多的信息。只需要提供指定了的信息即可。然后在Product 类库中我们提供一个产生ProductService实体的类ProductSharedService,中间只有一个Create方法,该方法返回ProductService实体。

public static class ProductSharedService
{public static ProductService Create(){return new ProductService();}
}

我们还需要在Shared类库中提供动态加载程序集,反射调用ProductSharedService的Create方法的通用方法。于是新建类SharedInterfaceProxy,通过构造函数传入程序集名称,以及创建实体类的类名称。

public class SharedInterfaceProxy
{private string _assemblyName;private string _className;private const string _methodName = "Create";private MethodInfo _proxcy;/// <summary>/// 构造函数/// </summary>/// <param name="assemblyName">程序集名称</param>/// <param name="className">程序集中产生实体类的类名称,该类必须存在一个名为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方法,使用反射调用该方法就可以产生创建一个服务实体。

现在,假设我们需要在Order类库中调用Product的GetProductsByOrderId 方法,现在我们可以通过 实例化SharedInterfaceProxy类,然后通过接口实现调用。

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

以上就是通过使用反射和接口实现了循环引用工程的解耦和,其基本原理就是设计模式中的依赖倒置原理,即高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。这里在本文之前Product依赖Order高层模块,Order也依赖Product高层模块,改造之后,两者都依赖于Shared中的接口这一抽象。

本来想画几个UML图的,这样就一目了然,可惜不太会,后面会补上。

希望本文对您了解如何简单的解决循环引用以及对设计模式中的依赖倒置有所理解。

从循环引用谈依赖倒置原则相关推荐

  1. C#软件设计——小话设计模式原则之:依赖倒置原则DIP

    前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做" ...

  2. DIP(依赖倒置原则),IoC(控制反转),DI(依赖注入)复习总结

    原文地址:http://blog.csdn.net/qqlinke/archive/2011/04/05/6303689.aspx ---- 概念 ---- ◆1.依赖倒置原则(DIP,Depende ...

  3. 依赖倒置原则_面向对象的设计原则你不要了解一下么?

    昨天我看了单一职责原则和开闭原则,今天我们再来看里式替换原则和依赖倒置原则,千万别小看这些设计原则,他在设计模式中会有很多体现,所以理解好设计原则之后,那么设计模式,也会让你更加的好理解一点. 前言 ...

  4. 软件设计原则之里氏替换原则、依赖倒置原则

    系列文章目录 软件设计原则之单一职责原则.开闭原则 软件设计原则之里氏替换原则.依赖倒置原则 软件设计原则之接口隔离原则.合成复用原则.迪米特原则 文章目录 系列文章目录 一.里氏替换原则 什么是里氏 ...

  5. Java依赖于抽象不依赖于具体,依赖倒置原则(Dependecy-Inversion Principle)

    依赖倒置原则(Dependence Inversion Principle,DIP)的原始定义: 高层模块不应该依赖底层模块,两者都应该依赖其抽象: 抽象不应该依赖细节: 细节应该依赖抽象. 抽象:即 ...

  6. 依赖倒置原则(Dependecy-Inversion Principle)

    依赖倒置原则(Dependence Inversion Principle,DIP)的原始定义: 高层模块不应该依赖底层模块,两者都应该依赖其抽象: 抽象不应该依赖细节: 细节应该依赖抽象. 抽象:即 ...

  7. 4.3 依赖倒置原则

    一. 依赖倒置原则的定义 1.核心思想 依赖倒置原则(Dependence Inversion Principle,DIP) *1.高层模块 不应该依赖 低层模块, 二者都应该依赖其抽象; *2.抽象 ...

  8. 吐槽 依赖倒置原则/DIP

    返回目录   1.2.5抛弃依赖倒置原则(正文) ,本文专门吐槽依赖倒置原则(Dependency Inversion Principle.DIP). 1.Robert C. Martin的那篇DIP ...

  9. 7.12 其他面向对象设计原则3: 依赖倒置原则DIP

    其他面向对象设计原则3: 依赖倒置原则DIP  The Dependency Inversion Principle 7.1 依赖倒置原则DIP The Dependency Inversion P ...

最新文章

  1. 基础知识——if语句和字典(四)
  2. pycharm设置回退功能
  3. 微信小程序 textarea 简易解决方案
  4. Luogu T24242 购物券Ⅰ(数据已加强)
  5. Maven知识点简要
  6. 第二章 PX4-RCS启动文件解析
  7. 一步步编写操作系统 30 cpu的分支预测简介
  8. 十二、Promise的学习笔记(Promise的基本使用、链式编程、all())
  9. wordpress 自定义分类url 重写_WordPress导航主题-WebStack导航主题
  10. Thrift 教程 开发 笔记 原理 资料 使用 范例 示例 应用
  11. 小林求职记(二):说好的问基础,为啥我感觉一点也不基础呢?
  12. linux下expect命令实现批量ssh免密
  13. gradle入门(1-7)eclipse和gradle集成插件的安装和使用
  14. delphi如何获得select得到的信息_如何建立闭环的笔记体系
  15. Java 判断中文及标点符号
  16. Spring Boot入门教程(三十六):支付宝集成-当面付
  17. 2021年阳江市高考成绩查询,阳江市召开2020年高考总结暨2021年新高考备考动员会...
  18. 什么打印软件比较好用?可以打印资料的网上打印平台
  19. JavaScript中用数组实现键值对
  20. MySQL数据库:创建一个表,并执行插入、修改和删除数据操作

热门文章

  1. [BZOJ2017][Usaco2009 Nov]硬币游戏
  2. javascript简单介绍
  3. java List接口
  4. [Python] MySQLdb(即 MySQL-python 包)在 OS X 中安装指南
  5. MySQL中的BLOB类型
  6. mysql并发写入性能分析
  7. android中在代码中动态布按钮和画板局并绘制曲线
  8. IOS约束三个按钮等宽等高,均分整个view,
  9. springboot 建readme_经验分享:给项目创建和编写README.md文件的步骤
  10. C语言算小数加减,C语言带小数加减乘除.doc