高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
1 案例分析(组装电脑)
组装一台电脑,需要CPU,硬盘,内存条等配件。这些配件又各自具有多种品牌,例如,CPU有Intel与AMD,硬盘有择希捷与西数,内存条有金士顿与海盗船。
using System;
namespace DesignPattern
{
internal static class Program
{
public static void Main(string[] args)
{
var c = new Computer
{
Cpu = new IntelCpu(),
Memory = new KingstonMemory(),
HardDisk = new XiJieHardDisk()
};
c.Run();
}
}
public class Computer
{
/// <summary>
/// Inter Cpu 属性
/// </summary>
public IntelCpu Cpu { get; set; }
/// <summary>
/// 金士顿 内存 属性
/// </summary>
public KingstonMemory Memory { get; set; }
/// <summary>
/// 希捷 硬盘 属性
/// </summary>
public XiJieHardDisk HardDisk { get; set; }
/// <summary>
/// 运行
/// </summary>
public void Run()
{
Console.WriteLine("运行计算机");
var data = this.HardDisk.Get();
Console.WriteLine("从硬盘上获取的数据是:" + data);
this.Cpu.Run();
this.Memory.Save();
}
}
/// <summary>
/// 希捷硬盘类
/// </summary>
public class XiJieHardDisk
{
/// <summary>
/// 保存数据
/// </summary>
/// <param name="data">数据</param>
public void Save(string data)
{
Console.WriteLine("使用希捷硬盘存储数据为:" + data);
}
/// <summary>
/// 获取数据
/// </summary>
/// <returns>数据</returns>
public string Get()
{
Console.WriteLine("使用希捷希捷硬盘取数据");
return "数据";
}
}
/// <summary>
/// 金士顿内存类
/// </summary>
public class KingstonMemory
{
/// <summary>
/// 保存数据
/// </summary>
public void Save()
{
Console.WriteLine("使用金士顿内存条");
}
}
/// <summary>
/// 因特尔CPU类
/// </summary>
public class IntelCpu
{
/// <summary>
/// 运行
/// </summary>
public void Run()
{
Console.WriteLine("使用Intel处理器");
}
}
}
上面组装的电脑能够正常运行,但配件的品牌是固定的,不可以改变。如果用户想使用自己中意的品牌,则上述设计是不满足要求的。
2 案例改进
根据依赖倒转原则进行改进:只需要修改Computer类,让Computer类依赖抽象(各个配件的接口)
,而不是依赖于各个组件具体的实现类。
using System;
namespace DesignPattern
{
internal static class Program
{
public static void Main(string[] args)
{
var c = new Computer()
{
Cpu = new IntelCpu(),
Memory = new KingstonMemory(),
HardDisk = new XiJieHardDisk()
};
c.Run();
}
}
public class Computer
{
/// <summary>
/// CPU属性
/// </summary>
public ICpu Cpu { get; set; }
/// <summary>
/// 内存属性
/// </summary>
public IMemory Memory { get; set; }
/// <summary>
/// 硬盘属性
/// </summary>
public IHardDisk HardDisk { get; set; }
/// <summary>
/// 运行
/// </summary>
public void Run()
{
Console.WriteLine("运行计算机");
var data = this.HardDisk.Get();
Console.WriteLine("从硬盘上获取的数据是:" + data);
this.Cpu.Run();
this.Memory.Save();
}
}
/// <summary>
/// 硬盘接口
/// </summary>
public interface IHardDisk
{
/// <summary>
/// 保存数据
/// </summary>
/// <param name="data">数据</param>
void Save(string data);
/// <summary>
/// 获取数据
/// </summary>
/// <returns>数据</returns>
string Get();
}
/// <summary>
/// 内存接口
/// </summary>
public interface IMemory
{
/// <summary>
/// 保存
/// </summary>
void Save();
}
/// <summary>
/// CPU接口
/// </summary>
public interface ICpu
{
/// <summary>
/// 运行
/// </summary>
void Run();
}
/// <summary>
/// 希捷硬盘类
/// </summary>
public class XiJieHardDisk : IHardDisk
{
/// <summary>
/// <inheritdoc cref="IHardDisk"/>
/// </summary>
/// <param name="data">数据</param>
public void Save(string data) => Console.WriteLine("使用希捷硬盘存储数据为:" + data);
/// <summary>
/// <inheritdoc cref="IHardDisk"/>
/// </summary>
/// <returns>数据</returns>
public string Get()
{
Console.WriteLine("使用希捷希捷硬盘取数据");
return "数据";
}
}
/// <summary>
/// 金士顿内存
/// </summary>
public class KingstonMemory : IMemory
{
/// <summary>
/// 保存
/// </summary>
public void Save() => Console.WriteLine("使用金士顿内存条");
}
/// <summary>
/// CPU
/// </summary>
public class IntelCpu : ICpu
{
/// <summary>
/// 运行
/// </summary>
public void Run() => Console.WriteLine("使用Intel处理器");
}
}
改进后,Computer
类的成员HardDisk
、Cpu
、Memory
可以使用实现IHardDisk
、ICpu
、IMemory
接口的任意类型。