依赖倒置原则
依赖倒置原则:上层模块不应该依赖于低层模块,二者应该通过抽象依赖,就是说应该依赖于抽象,而不是依赖于细节,面向抽象编程。
看下面的代码:
学生类:
public class Student { public int Id { get; set; } public string Name { get; set; } public void PlayiPhone(iPhone phone) { Console.WriteLine("这里是{0}", this.Name); phone.Call(); phone.Text(); } public void PlayLumia(Lumia phone) { Console.WriteLine("这里是{0}", this.Name); phone.Call(); phone.Text(); } }
手机抽象类:
public abstract class AbstractPhone { public int Id { get; set; } public string Branch { get; set; } public abstract void Call(); public abstract void Text(); }
各种手机类:
/// <summary> /// iPhone /// </summary> public class iPhone : AbstractPhone { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } } public class Lumia : AbstractPhone { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Text", this.GetType().Name); } public void New() { } }
调用:
public class DIPShow { public static void Show() { Console.WriteLine("**************DIPShow***************"); //Student 为上层 手机为低层 因为学生要用手机,所以学生是处于高层的 Student student = new Student() { Id = 191, Name = "候鸟班长" }; { iPhone phone = new iPhone(); student.PlayiPhone(phone); } { Lumia phone = new Lumia(); student.PlayLumia(phone); } } }
从上面的代码逻辑中我们可以得知,Student 为上层,手机为低层 因为学生要用手机,所以学生是处于高层的。依赖倒置原则又要求我们说高层不能直接依赖于底层,二者要通过抽象进行依赖,现在的代码也能实现功能,但是后期如果说要增加不同的手机类别的话,在学生类中,就要不断改动学生类,增加学生类中使用手机的方法,此时手机和学生是依赖于细节的,所以我们需要将二者依赖于抽象,这样的话后期不管增加多少中手机品类都不用更改学生类,因为手机的变化本来就不应该和学生有联系,现在的耦合很严重,改动如下:
学生类:
public class Student { private AbstractPhone phone = null; public Student(AbstractPhone phone) { this.phone = phone; } public int Id { get; set; } public string Name { get; set; } public void Play() { Console.WriteLine("这里是{0}", this.Name); this.phone.Call(); this.phone.Text(); } }
调用:
public class DIPShow { public static void Show() { Console.WriteLine("**************DIPShow***************"); //Student 为上层 手机为低层 因为学生要用手机,所以学生是处于高层的 Student student = new Student(new iPhone()) { Id = 191, Name = "候鸟班长" }; student.Play(); } }
这样就实现依赖于抽象,耦合降低了。
如果你的东西根本不会变化了,那就不需要考虑依赖倒置原则。
好处:扩展性更高了,
接口隔离原则
接口隔离原则:客户端不应该依赖它不需要的接口; 一个类对另一个类的依赖应该建立在最小的接口上;或者说实现一个接口的时候,只需要自己必须的功能。
接口就是作为一个约束,必须实现全部的接口。
看下面的方法,手机继承了一个基类,这个基类是作为手机存在必须要实现存在的功能,是作为这个物品本质上存在的属性和行为,但是手机也有不同的,也存在不同的功能,所以我们可以通过接口来定义并实现它们:
public abstract class AbstractPhone { public int Id { get; set; } public string Branch { get; set; } public abstract void Call(); public abstract void Text(); } /// <summary> /// 一个大而全的接口 /// </summary> public interface IExtend { void Photo(); void Online(); void Game(); void Record(); void Movie(); void Map(); void Pay(); }
华为手机:
public class Honor : AbstractPhone, IExtend { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } public void Photo() { Console.WriteLine("User {0} Photo", this.GetType().Name); } public void Online() { Console.WriteLine("User {0} Online", this.GetType().Name); } public void Game() { Console.WriteLine("User {0} Game", this.GetType().Name); } public void Map() { Console.WriteLine("User {0} Map", this.GetType().Name); } public void Pay() { Console.WriteLine("User {0} Pay", this.GetType().Name); } public void Record() { Console.WriteLine("User {0} Record", this.GetType().Name); } public void Movie() { Console.WriteLine("User {0} Movie", this.GetType().Name); } }
IPhone:
/// <summary> /// OldMan 老人机 /// </summary> public class OldMan : AbstractPhone, IExtend { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } public void Photo() { Console.WriteLine("User {0} Photo", this.GetType().Name); } public void Online() { Console.WriteLine("User {0} Online", this.GetType().Name); } public void Game() { Console.WriteLine("User {0} Game", this.GetType().Name); } public void Map() { Console.WriteLine("User {0} Map", this.GetType().Name); } public void Pay() { Console.WriteLine("User {0} Pay", this.GetType().Name); } public void Record() { Console.WriteLine("User {0} Record", this.GetType().Name); } public void Movie() { Console.WriteLine("User {0} Movie", this.GetType().Name); } }
但是现在有一个问题,接口作为一个约束,必须要实现它的所有方法,但是老人机没办法全部实现的,因为老人机没有导航功能,也没有支付功能等,IExtend接口功能太全了,所以我们应该拆分一下,保证它的灵活性。接口是能够多实现的,但是继承只能单继承。
/// <summary> /// /// </summary> public interface IExtend { void Photo(); void Online(); void Game(); } public interface IExtendAdvanced { void Record(); void Movie(); void Map(); void Pay(); }
手机类:
/// <summary> /// iPhone /// </summary> public class iPhone : AbstractPhone, IExtend, IExtendAdvanced { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } public void Photo() { Console.WriteLine("User {0} Photo", this.GetType().Name); } public void Online() { Console.WriteLine("User {0} Online", this.GetType().Name); } public void Game() { Console.WriteLine("User {0} Game", this.GetType().Name); } public void Map() { Console.WriteLine("User {0} Map", this.GetType().Name); } public void Pay() { Console.WriteLine("User {0} Pay", this.GetType().Name); } public void Record() { Console.WriteLine("User {0} Record", this.GetType().Name); } public void Movie() { Console.WriteLine("User {0} Movie", this.GetType().Name); } } /// <summary> /// OldMan 老人机 /// </summary> public class OldMan : AbstractPhone, IExtend { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } public void Photo() { Console.WriteLine("User {0} Photo", this.GetType().Name); } public void Online() { Console.WriteLine("User {0} Online", this.GetType().Name); } public void Game() { Console.WriteLine("User {0} Game", this.GetType().Name); } }
比如说,我又新加了一个相机,相机默认只能拍照和上网,我再次进行接口的拆分:
/// <summary> /// /// </summary> public interface IExtend { void Game(); } public interface IExtendAdvanced { void Record(); void Movie(); void Map(); void Pay(); } /// <summary> /// 拆分出来的 相机所具有的方法 IExtend接口方法又少了 /// </summary> public interface IExtendPhontography { void Photo(); void Online(); }
手机相机类:
/// <summary> /// iPhone /// </summary> public class iPhone : AbstractPhone, IExtend, IExtendAdvanced, IExtendPhontography { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } public void Photo() { Console.WriteLine("User {0} Photo", this.GetType().Name); } public void Online() { Console.WriteLine("User {0} Online", this.GetType().Name); } public void Game() { Console.WriteLine("User {0} Game", this.GetType().Name); } public void Map() { Console.WriteLine("User {0} Map", this.GetType().Name); } public void Pay() { Console.WriteLine("User {0} Pay", this.GetType().Name); } public void Record() { Console.WriteLine("User {0} Record", this.GetType().Name); } public void Movie() { Console.WriteLine("User {0} Movie", this.GetType().Name); } } /// <summary> /// OldMan 老人机 /// </summary> public class OldMan : AbstractPhone, IExtend { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } public void Photo() { Console.WriteLine("User {0} Photo", this.GetType().Name); } public void Online() { Console.WriteLine("User {0} Online", this.GetType().Name); } public void Game() { Console.WriteLine("User {0} Game", this.GetType().Name); } } /// <summary> ///相机 假设相机只能拍照和上网 /// </summary> public class Camera : IExtendPhontography { /// <summary> /// 拍照 /// </summary> public void Photo() { Console.WriteLine("User {0} Photo", this.GetType().Name); } /// <summary> /// 录音 /// </summary> public void Online() { Console.WriteLine("User {0} Online", this.GetType().Name); } }
为什么分要对接口进行拆分呢?比如说新进相机之后,我不再拆分IExtend接口,我新建一个接口IExtendPhontographyTest,IExtend接口不动:
/// <summary> /// /// </summary> public interface IExtend { void Game(); void Photo(); void Online(); } public interface IExtendAdvanced { void Record(); void Movie(); void Map(); void Pay(); } /// <summary> /// 拆分出来的 相机所具有的方法 IExtend接口方法又少了 /// </summary> public interface IExtendPhontographyTest { void Photo(); void Online(); }
实现类:
/// <summary> ///相机 假设相机只能拍照和上网 /// </summary> public class Camera : IExtendPhontographyTest { /// <summary> /// 拍照 /// </summary> public void Photo() { Console.WriteLine("User {0} Photo", this.GetType().Name); } /// <summary> /// 录音 /// </summary> public void Online() { Console.WriteLine("User {0} Online", this.GetType().Name); } }
这样功能上是没问题的,但是这样的话就少了复用性,但是如果使用拆分接口的形式的话,就能够实现复用性:
public class ISPShow { public static void Show() { IExtendPhontography phontography = new iPhone(); IExtendPhontography camara = new Camera(); Photography(camara); Photography(phontography); } /// <summary> /// 只要是实现了这个接口IExtendPhontography的类都可以调用这个方法 /// </summary> /// <param name="extend"></param> public static void Photography(IExtendPhontography extend) { extend.Photo(); extend.Online(); } }
IPhone和camera类都实现了IExtendPhontography类,所以也都能调用这个方法,但是如果只是另外新建了一个类的话,这个方法就只能相机类能用了。
所以说,接口隔离原则希望我们能够尽量将接口拆分一下,保证它的灵活性,只依赖于自己本身需要的接口,也是依赖倒置的延续,复用性更强,都是为了未来更好 的扩展开放, c#中很多封装的类都是这种形式,实现多接口比如List,Dictionary。
最后,为什么我们要拆分接口呢?因为不能让类型实现自己没有的功能,也能让方法具备复用性。
究竟如何设计呢?
1 接口不能太大,否则会实现不需要的功能
2 接口尽量的小,但是一致的功能应该在一起,也不能过度设计。
3 接口合并:如果一个业务包含多个步骤,我们不能把步骤都暴露,而是提供一个入口即可。比如手机定位,定位肯定要有很多步骤,不能都开放出来,只留出一个定位的接口就行。
开闭原则
开闭原则:对扩展开发,对修改关闭。如果有功能扩展变化的需求,希望是增加而不是修改 ,尽量不影响之前的功能
IExtendPhontographyTest