依赖倒置原则就是抽象类和接口在使用时的一些规范和建议,我们的应用上层模块不直接依赖于下层模块,不具体依赖某个类或者对象,而是依赖于某个抽象。
接着上一节的Demo,我们有3个手机的对象。分别是LumiaPhone,Galaxy,ApplePhone,现在我们新增一个Student学生类,学生会使用手机。有可能使用LumiaPhone手机,也有可能使用Galaxy,也有可能使用
ApplePhone,那我们的Student看起来的得实现成这样:
public class Student { public int Id { get; set; } public string Name { get; set; } public void PlayPhone(LumiaPhone lumiaPhone) { Console.WriteLine($"{Name} use {lumiaPhone.Name}"); } public void PlayApplePhone(ApplePhone applePhone) { Console.WriteLine($"{Name} use {applePhone.Name}"); } public void PlayGalaxy(Galaxy galaxy) { Console.WriteLine($"{Name} use {galaxy.Name}"); } }
具体使用时,我们可能会是这样:
先创建一个Student再根据学生使用的某种手机 调用不同的play方法
LumiaPhone lumiaPhone = new LumiaPhone(); ApplePhone applePhone = new ApplePhone(); Galaxy galaxy = new Galaxy(); Student student = new Student(); student.PlayPhone(lumiaPhone); student.PlayApplePhone(applePhone); student.PlayGalaxy(galaxy);
这个时候假设Student旧的手机坏了,换了新的华为手机,那我们新增一个Honor对象
public class Honor { public void System() { Console.WriteLine("Hua Wei Honor"); } }
再修改Student类 新增一个方法
public void PlayHonor(Honor honor) { Console.WriteLine($"{Name} use {honor.Name}"); }
使用时
Honor honor = new Honor(); student.PlayHonor(honor);
手机有太多种类,每次新增对象我们都要在Student中新增方法 在调用处进行手机的实例化,维护起来
非常麻烦,容易出错。不利于扩展,所以我们的Student 不应该依赖于具体的类,而是应该依赖抽象
上一遍提到的BasePhone,我们来改造下代码
Student会变成这样
public class Student { public int Id { get; set; } public string Name { get; set; } public void PlayPhone(BasePhone basePhone) { Console.WriteLine($"{Name} use {basePhone.Name}"); } //public void PlayPhone(LumiaPhone lumiaPhone) //{ // Console.WriteLine($"{Name} use {lumiaPhone.Name}"); //} //public void PlayApplePhone(ApplePhone applePhone) //{ // Console.WriteLine($"{Name} use {applePhone.Name}"); //} //public void PlayGalaxy(Galaxy galaxy) //{ // Console.WriteLine($"{Name} use {galaxy.Name}"); //} //public void PlayHonor(Honor honor) //{ // Console.WriteLine($"{Name} use {honor.Name}"); //} }
我们的调用处
static void Main(string[] args)
{
{
LumiaPhone lumiaPhone = new LumiaPhone();
ApplePhone applePhone = new ApplePhone();
Galaxy galaxy = new Galaxy();
Honor honor = new Honor();
Student student = new Student();
student.PlayPhone(lumiaPhone);
student.PlayPhone(applePhone);
student.PlayPhone(galaxy);
student.PlayPhone(honor);
//student.PlayPhone(lumiaPhone);
//student.PlayApplePhone(applePhone);
//student.PlayGalaxy(galaxy);
//student.PlayHonor(honor);
}
Console.ReadKey();
}
这个时候感觉Main还是依赖了具体的对象Student,以学生使用honor手机为例
BasePhone honor = new Honor(); Student student = new Student(); student.PlayPhone(honor);
我们新增一个SimpleFactory及接口IPlayPhone
public static class SimpleFactory { public static BasePhone CreatePhone() { return new Honor(); } public static Student CreateStudent() { return new Student(); } }
public interface IPlayPhone { void PlayPhone(BasePhone basePhone); }
那我们的Main方法则变成
static void Main(string[] args) { { BasePhone honor = SimpleFactory.CreatePhone(); IPlayPhone student = SimpleFactory.CreateStudent(); student.PlayPhone(honor); //Honor honor = new Honor(); //Student student = new Student(); //student.PlayPhone(honor); //student.PlayPhone(lumiaPhone); //student.PlayApplePhone(applePhone); //student.PlayGalaxy(galaxy); //student.PlayHonor(honor); } Console.ReadKey(); }
我在这个demo SimpleFactory里直接返回了一个对象,项目中是可以通过读取配置文件反射来构造实的
这样只要通过改配置文件 就可以实现学生更换手机的功能,同样通过配置文件 也可以实现 老师使用手机只要我们新增一个Teacher类 实现IPlayPhone接口。其他代码不需要任何改动, 这就是我们原则 依赖抽象或接口 而不应该依赖于具体的细节的好处, 这里学生,老师其实就是上层,而我们的手机则是下层,上层不应该依赖下层 而是应该依赖抽象