当前位置:C++技术网 > 资讯 > 软件设计之设计模式初接触之依赖倒置原则

软件设计之设计模式初接触之依赖倒置原则

更新时间:2017-05-24 09:11:05浏览次数:1+次

依赖倒置原则(DIP)在很大程度上主要是为了解耦,即软件设计中的"高内聚,低耦合"原则.

    之前学习ASP.NET MVC的时候,接触到了Controller的创建过程,其实就是利用IOC来实现,那会对依赖倒置设计模式很模糊,这次学习Attribute 特性 的时候找到了比较详细的相关资料,做个笔记.

官方解释:高层模块不应该依赖底层模块,两者都应该依赖于抽象;抽象不应该依赖细节,细节应该依赖抽象.

1)高层模块不应该直接依赖于底层模块的具体实现,而应该依赖于底层的抽象。换言之,模块间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。

2)接口和抽象类不应该依赖于实现类,而实现类依赖接口或抽象类。这一点其实不用多说,很好理解,“面向接口编程”思想正是这点的最好体现。

相比传统的软件设计架构,比如我们常说的经典的三层架构,UI层依赖于BLL层,BLL层依赖于DAL层。由于每一层都是依赖于下层的实现,这样当某一层的结构发生变化时,它的上层就不得不也要发生改变,比如我们DAL里面逻辑发生了变化,可能会导致BLL和UI层都随之发生变化,这种架构是非常荒谬的!好,这个时候如果我们换一种设计思路,高层模块不直接依赖低层的实现,而是依赖于低层模块的抽象,具体表现为我们增加一个IBLL层,里面定义业务逻辑的接口,UI层依赖于IBLL层,BLL层实现IBLL里面的接口,所以具体的业务逻辑则定义在BLL里面,这个时候如果我们BLL里面的逻辑发生变化,只要接口的行为不变,上层UI里面就不用发生任何变化。

在经典的三层里面,高层模块直接依赖低层模块的实现,当我们将高层模块依赖于底层模块的抽象时,就好像依赖“倒置”了。这就是依赖倒置的由来。通过依赖倒置,可以使得架构更加稳定、更加灵活、更好应对需求变化。

上面说了,在三层架构里面增加一个接口层能实现依赖倒置,它的目的就是降低层与层之间的耦合,使得设计更加灵活。从这点上来说,依赖倒置原则也是“松耦合”设计的很好体现。

下面看一段程序:(原文摘自Attribute在.NET编程中的应用(五))
//Inventory.cs
using System;
using System.Collections;

namespace NiwalkerDemo
{
 public class Inventory
 {

 private Hashtable inventory=new Hashtable();
 
 public Inventory()
 {
 inventory["Item1"]=100;
 inventory["Item2"]=200;
 }

 public bool Checkout(string product, int quantity)
 {
 int qty=GetQuantity(product);
      return qty>=quantity;
 }
 
 public int GetQuantity(string product)
 {
 int qty=0;
 if(inventory[product]!=null)
 qty = (int)inventory[product];
 return qty;
 }
 
 public void Update(string product, int quantity)
 {
 int qty=GetQuantity(product);
 inventory[product]=qty-quantity;
 }
 }
}

//Logbook.cs
using System;

namespace NiwalkerDemo
{
 public class Logbook
 {
 public static void Log(string logData)
 {
 Console.WriteLine("log:{0}",logData);
 }
 }
}

//Order.cs
using System;

namespace NiwalkerDemo
{
 public class Order
 {
 private int orderId;
 private string product;
 private int quantity;
 
 public Order(int orderId)
 {
 this.orderId=orderId;
 }
 
 public void Submit()
 {
 Inventory inventory=new Inventory(); //创建库存对象
 
 //检查库存
 if(inventory.Checkout(product,quantity))
 {
 Logbook.Log("Order"+orderId+" available");
 inventory.Update(product,quantity);
 }
 else
 {
 Logbook.Log("Order"+orderId+" unavailable");
 SendEmail();
 }
 }
 
 public string ProductName
 {
 get{ return product; }
 set{ product=value; }
 }
 
 public int OrderId
 {
 get{ return orderId; }
 }
 
 public int Quantity
 {
 get{ return quantity;}
 set{ quantity=value; }
 }
 
 public void SendEmail()
 {
 Console.WriteLine("Send email to manager");
 }
 }
}


下面是调用程序: 
//AppMain.cs

using System;

namespace NiwalkerDemo
{
 public class AppMain
 {
 static void Main()
 {
 Order order1=new Order(100);
 order1.ProductName="Item1";
 order1.Quantity=150;
 order1.Submit();
 
 Order order2=new Order(101);
 order2.ProductName="Item2";
 order2.Quantity=150;
 order2.Submit();
 }
 }
}

就像原文说的:
程序看上去还不错,商务对象封装了商务规则,运行的结果也符合要求。但是我好像听到你在抱怨了,没有吗?当你的客户的需求改变的时候(客户总是经常改变他们的需求),比如库存检查的规则不是单一的检查产品的数量,还要检查产品是否被预订的多种情况,那么你需要改变Inventory的代码,同时还要修改Order中的代码,我们的例子只是一个简单的商务逻辑,实际的情况比这个要复杂的多。问题在于Order对象同其他的对象之间是紧耦合的,从OOP的观点出发,这样的设计是有问题的,如果你写出这样的程序,至少不会在我的团队里面被Pass.

下面,我们在依赖倒置设计模式的基础上重构代码:
接口部分:

    

public interface IInventoryHandle
 {
bool Checkout(string product, int quantity);
int GetQuantity(string product);
void Update(string product, int quantity);
 }

public interface IOrderHandle
 {
 int orderId { get; set; }
 string product { get; set; }
 int quantity { get; set; }

 void Submit();
 void SendEmail();
 }
调用部分:
public class Inventory : IInventoryHandle
 {
 public Hashtable inventory = new Hashtable();

 public Inventory()
 {
 inventory["Item1"] = 100;
 inventory["Item2"] = 200;

 }

 public bool Checkout(string product, int quantity)
 {
 int qty = GetQuantity(product);
 return qty >= quantity;
 }

 public int GetQuantity(string product)
 {
 int qty = 0;
 if (inventory[product] != null)
 qty = (int)inventory[product];
 return qty;
 }

 public void Update(string product, int quantity)
 {
 int qty = GetQuantity(product);
 inventory[product] = qty - quantity;
 }

 }


 public class Logbook
 {
 public static void Log(string logData)
 {
 Console.WriteLine("log:{0}", logData);
 }
 }

 public class Order : IOrderHandle,IDisposable
 {
 public IInventoryHandle _inventory;

 public Order(IInventoryHandle inventory, int _orderId)
 {
 _inventory = inventory;
 orderId = _orderId;
 }

 public void Submit()
 {
 //检查库存
 if (_inventory.Checkout(product, quantity))
 {
 Logbook.Log("Order" + orderId + " available");
 _inventory.Update(product, quantity);
 }
 else
 {
 Logbook.Log("Order" +orderId + " unavailable");
 SendEmail();
 }
 }

 public void SendEmail()
 {
 Console.WriteLine("Send email to manager");
 }

 public int orderId
 {
 get;
 set;
 }

 public string product
 {
 get;
 set;
 }

 public int quantity
 {
 get;
 set;
 }

public void Dispose()
 {
 
 }
 }

 class Program
 {
 static void Main(string[] args)
 {
 var _order1 = new Order(new Inventory(),100); 
 _order1.product = "Item1";
 _order1.quantity = 150;
 
 _order1.Submit();
 _order1.Dispose();

 var _order2 = new Order(new Inventory(), 250); 
 _order2.product = "Item2";
 _order2.quantity = 150;

 _order2.Submit();
 _order2.Dispose();

 Console.Read();
 }
 }
参考: http://www.cnblogs.com/landeanfen/p/5169163.html    http://blog.csdn.net/niwalker/article/details/8871