接口隔离原则,英文缩写ISP,全称Interface Segregation Principle。架构
原始定义:Clients should not be forced to depend upon interfaces that they don't use,还有一种定义是The dependency of one class to another one should depend on the smallest possible interface。优化
官方翻译:其一是不该该强行要求客户端依赖于它们不用的接口;其二是类之间的依赖应该创建在最小的接口上面。简单点说,客户端须要什么功能,就提供什么接口,对于客户端不须要的接口不该该强行要求其依赖;类之间的依赖应该创建在最小的接口上面,这里最小的粒度取决于单一职责原则的划分。翻译
从功能上来看,接口隔离和单一职责两个原则具备必定的类似性。其实若是咱们仔细想一想仍是有区别的。设计
(1)从原则约束的侧重点来讲,接口隔离原则更关注的是接口依赖程度的隔离,更加关注接口的“高内聚”;而单一职责原则更加注重的是接口职责的划分。code
(2)从接口的细化程度来讲,单一职责原则对接口的划分更加精细,而接口隔离原则注重的是相同功能的接口的隔离。接口隔离里面的最小接口有时能够是多个单一职责的公共接口。接口
(3)单一职责原则更加偏向对业务的约束,接口隔离原则更加偏向设计架构的约束。这个应该好理解,职责是根据业务功能来划分的,因此单一原则更加偏向业务;而接口隔离更可能是为了“高内聚”,偏向架构的设计。ip
下面就以咱们传统行业的订单操做为例来讲明下接口隔离的必要性。ci
软件设计最初,咱们的想法是相同功能的方法放在同一个接口里面,以下,全部订单的操做都放在订单接口IOrder里面。理论上来讲,这貌似没错。咱们来看看如何设计。get
public interface IOrder { //订单申请操做 void Apply(object order); //订单审核操做 void Approve(object order); //订单结束操做 void End(object order); }
刚开始只有销售订单,咱们只须要实现这个接口就行了。it
public class SaleOrder:IOrder { public void Apply(object order) { throw new NotImplementedException(); } public void Approve(object order) { throw new NotImplementedException(); } public void End(object order) { throw new NotImplementedException(); } }
后来,随着系统的不断扩展,咱们须要加入生产订单,生产订单也有一些单独的接口方法,好比:排产、冻结、导入、导出等操做。因而咱们向订单的接口里面继续加入这些方法。因而订单的接口变成这样:
public interface IOrder { //订单申请操做 void Apply(object order); //订单审核操做 void Approve(object order); //订单结束操做 void End(object order); //订单下发操做 void PlantProduct(object order); //订单冻结操做 void Hold(object order); //订单删除操做 void Delete(object order); //订单导入操做 void Import(); //订单导出操做 void Export(); }
咱们生产订单的实现类以下
//生产订单实现类 public class ProduceOrder : IOrder { /// <summary> /// 对于生产订单来讲无用的接口 /// </summary> /// <param name="order"></param> public void Apply(object order) { throw new NotImplementedException(); } /// <summary> /// 对于生产订单来讲无用的接口 /// </summary> /// <param name="order"></param> public void Approve(object order) { throw new NotImplementedException(); } /// <summary> /// 对于生产订单来讲无用的接口 /// </summary> /// <param name="order"></param> public void End(object order) { throw new NotImplementedException(); } public void PlantProduct(object order) { Console.WriteLine("订单下发排产"); } public void Hold(object order) { Console.WriteLine("订单冻结"); } public void Delete(object order) { Console.WriteLine("订单删除"); } public void Import() { Console.WriteLine("订单导入"); } public void Export() { Console.WriteLine("订单导出"); } }
销售订单的实现类也要相应作修改
//销售订单实现类 public class SaleOrder:IOrder { public void Apply(object order) { Console.WriteLine("订单申请"); } public void Approve(object order) { Console.WriteLine("订单审核处理"); } public void End(object order) { Console.WriteLine("订单结束"); } #region 对于销售订单无用的接口方法 public void PlantProduct(object order) { throw new NotImplementedException(); } public void Hold(object order) { throw new NotImplementedException(); } public void Delete(object order) { throw new NotImplementedException(); } public void Import() { throw new NotImplementedException(); } public void Export() { throw new NotImplementedException(); } #endregion }
需求作完了,上线正常运行。貌似问题也不大。系统运行一段时间以后,新的需求变动来了,要求生成订单须要一个订单撤销排产的功能,那么咱们的接口是否是就得增长一个订单撤排的接口方法CancelProduct。因而乎接口变成这样:
public interface IOrder { //订单申请操做 void Apply(object order); //订单审核操做 void Approve(object order); //订单结束操做 void End(object order); //订单下发操做 void PlantProduct(object order); //订单撤排操做 void CancelProduct(object order); //订单冻结操做 void Hold(object order); //订单删除操做 void Delete(object order); //订单导入操做 void Import(); //订单导出操做 void Export(); }
这个时候问题就来了,咱们的生产订单只要实现这个撤销的接口貌似就OK了,可是咱们的销售订单呢,原本销售订单这一块咱们不想作任何的变动,但是因为咱们IOrder接口里面增长了一个方法,销售订单的实现类是否是也必需要实现一个无效的接口方法?这就是咱们常说的“胖接口”致使的问题。因为接口过“胖”,每个实现类依赖了它们不须要的接口,使得层与层之间的耦合度增长,结果致使了不须要的接口发生变化时,实现类也不得不相应的发生改变。这里就凸显了咱们接口隔离原则的必要性,下面咱们就来看看如何经过接口隔离来解决上述问题。
咱们将IOrder接口分红两个接口来设计
//删除订单接口 public interface IProductOrder { //订单下发操做 void PlantProduct(object order); //订单撤排操做 void CancelProduct(object order); //订单冻结操做 void Hold(object order); //订单删除操做 void Delete(object order); //订单导入操做 void Import(); //订单导出操做 void Export(); } //销售订单接口 public interface ISaleOrder { //订单申请操做 void Apply(object order); //订单审核操做 void Approve(object order); //订单结束操做 void End(object order); }
对应的实现类只须要实现本身须要的接口便可
//生产订单实现类 public class ProduceOrder : IProductOrder { public void PlantProduct(object order) { Console.WriteLine("订单下发排产"); } public void CancelProduct(object order) { Console.WriteLine("订单撤排"); } public void Hold(object order) { Console.WriteLine("订单冻结"); } public void Delete(object order) { Console.WriteLine("订单删除"); } public void Import() { Console.WriteLine("订单导入"); } public void Export() { Console.WriteLine("订单导出"); } } //销售订单实现类 public class SaleOrder : ISaleOrder { public void Apply(object order) { Console.WriteLine("订单申请"); } public void Approve(object order) { Console.WriteLine("订单审核处理"); } public void End(object order) { Console.WriteLine("订单结束"); } }
这样设计就能完美解决上述“胖接口”致使的问题,若是须要增长订单操做,只须要在对应的接口和实现类上面修改便可,这样就不存在依赖不须要接口的状况。经过这种设计,下降了单个接口的复杂度,使得接口的“内聚性”更高,“耦合性”更低。由此能够看出接口隔离原则的必要性。
经过以上订单功能的优化,咱们看到了接口隔离原则的必要性,固然,关于接口隔离原则和单一职责原则的细节咱们也没必要过多追究,无论何种原则,能解决咱们的设计问题就是好的原则、咱们必须遵照的原则。