设计模式6大原则-依赖倒置原则
定义:
(1) 高层模块不应该依赖低层模块,两者都应该依赖其抽象
(2) 抽象不应该依赖细节
(3) 细节应该依赖抽象
如果高层模块依赖于低层模块,当低层模块发生变化需要改动时,会导致高层模块跟着修改。
抽象和细节又是什么?
抽象:接口或者抽象类
细节:就是实现类
优点:
(1) 降低类间的耦合
(2) 提高系统的稳定性
(3) 减少并行开发引起的风险
(4) 提高代码的可读性和可维护性
实现方法:
(1) 每个类尽量提供接口或抽象,或者两者都具备
(2) 变量的表面类型尽量是接口或者抽象类
(3) 任何类都不应该从具体类派生
(4) 继承时尽量遵循里氏替换原则
下面以一个实例展示依赖倒置的实现
饭店有不同的菜肴,来自五湖四海的顾客喜欢吃的家乡菜也不一样,如何优雅地实现顾客与菜肴的设计??
将菜肴抽象出一个接口,具体菜肴类实现该接口
顾客抽象出一个抽象类,具体顾客类继承抽象顾客类,抽象顾客类填加对菜肴抽象接口的引用
类图如下
菜肴接口
// 菜肴接口
public interface IFood
{
string GetName();
}
具体菜肴类
// 川菜麻婆豆腐
public class MaPoDouFu : IFood
{
public MaPoDouFu()
{
}
public string GetName()
{
return "川菜麻婆豆腐";
}
}
// 鲁菜红焖大虾
public class HongMenDaXia : IFood
{
public HongMenDaXia()
{
}
public string GetName()
{
return "红焖大虾";
}
}
抽象顾客类
// 顾客抽象类
public abstract class Customer
{
// 菜肴引用
public List<IFood> foodList = new List<IFood>();
// 添加菜肴
public virtual void SetFood(IFood food)
{
foodList.Add(food);
}
public abstract void Eat();
}
具体客户类
// 山东顾客
public class ShanDongCustomer : Customer
{
public override void Eat()
{
Console.WriteLine("我是山东的,我要吃山东的家乡菜");
foreach(var food in foodList)
{
Console.WriteLine(food.GetName() + "\n");
}
}
}
// 四川顾客
public class SiChuanCustomer : Customer
{
public override void SetFood(IFood food)
{
foodList.Add(food);
}
public override void Eat()
{
Console.WriteLine("我是四川的,我要吃四川的家乡菜");
foreach (var food in foodList)
{
Console.WriteLine(food.GetName() + "\n");
}
}
}
代码调用如下
public class Client
{
public Client()
{
MaPoDouFu maPoDouFu = new MaPoDouFu();
SiChuanCustomer siChuanCustomer = new SiChuanCustomer();
siChuanCustomer.SetFood(maPoDouFu);
siChuanCustomer.Eat();
HongMenDaXia hongMenDaXia = new HongMenDaXia();
ShanDongCustomer shanDongCustomer = new ShanDongCustomer();
shanDongCustomer.SetFood(hongMenDaXia);
shanDongCustomer.Eat();
}
}
运行结果如下