C#软件设计——小话设计模式原则之:接口隔离原则ISP

前言:有朋友问我,设计模式原则这些东西在园子里都讨论烂了,一搜一大把的资料,还花这么大力气去整这个干吗。博主不得不认可,园子里确实不少这方面的文章,而且不乏出色的博文。博主的想法是,既然要完善知识体系,就不能半途而废。今天就来看看设计模式原则的另外一个:接口隔离原则。html

软件设计原则系列文章索引设计模式

1、原理介绍

一、官方定义

接口隔离原则,英文缩写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。post

官方翻译:其一是不该该强行要求客户端依赖于它们不用的接口;其二是类之间的依赖应该创建在最小的接口上面。简单点说,客户端须要什么功能,就提供什么接口,对于客户端不须要的接口不该该强行要求其依赖;类之间的依赖应该创建在最小的接口上面,这里最小的粒度取决于单一职责原则的划分。
优化

二、本身理解

2.一、原理解释

  • 不该该强行要求客户端依赖于它们不用的接口。语句很好理解,即客户端须要什么接口,就依赖什么接口,不须要的就不依赖。那么咱们反过来讲,若是客户端依赖了它们不须要的接口,那么这些客户端程序就面临不须要的接口变动引发的客户端变动的风险,这样就会增长客户端和接口之间的耦合程度,显然与“高内聚、低耦合”的思想相矛盾。
  • 类之间的依赖应该创建在最小的接口上面。何为最小的接口,即可以知足项目需求的类似功能做为一个接口,这样设计主要就是为了“高内聚”。那么咱们如何设计最小的接口呢?那就要说说粒度的划分了,粒度细化的程度取决于咱们上一章讲的的单一职责原则里面接口划分的粒度。从这一点来讲,接口隔离和单一职责两个原则有必定的类似性。

2.二、接口隔离原则和单一职责原则

从功能上来看,接口隔离和单一职责两个原则具备必定的类似性。其实若是咱们仔细想一想仍是有区别的。url

(1)从原则约束的侧重点来讲,接口隔离原则更关注的是接口依赖程度的隔离,更加关注接口的“高内聚”;而单一职责原则更加注重的是接口职责的划分。spa

(2)从接口的细化程度来讲,单一职责原则对接口的划分更加精细,而接口隔离原则注重的是相同功能的接口的隔离。接口隔离里面的最小接口有时能够是多个单一职责的公共接口。翻译

(3)单一职责原则更加偏向对业务的约束,接口隔离原则更加偏向设计架构的约束。这个应该好理解,职责是根据业务功能来划分的,因此单一原则更加偏向业务;而接口隔离更可能是为了“高内聚”,偏向架构的设计。设计

2、场景示例

下面就以订单的操做为例来讲明下接口隔离的必要性。code

一、胖接口

 软件设计最初,咱们的想法是相同功能的方法放在同一个接口里面,以下,全部订单的操做都放在订单接口IOrder里面。理论上来讲,这貌似没错。咱们来看看如何设计。

   public interface IOrder
    {
        //订单申请操做
        void Apply(object order);

        //订单审核操做
        void Approve(object order);

        //订单结束操做
        void End(object order);

    }

刚开始只有销售订单,咱们只须要实现这个接口就行了。

    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("订单结束");
        }
    }

这样设计就能完美解决上述“胖接口”致使的问题,若是须要增长订单操做,只须要在对应的接口和实现类上面修改便可,这样就不存在依赖不须要接口的状况。经过这种设计,下降了单个接口的复杂度,使得接口的“内聚性”更高,“耦合性”更低。由此能够看出接口隔离原则的必要性。

3、总结

经过以上订单功能的优化,咱们看到了接口隔离原则的必要性,固然,关于接口隔离原则和单一职责原则的细节咱们也没必要过多追究,无论何种原则,能解决咱们的设计问题就是好的原则、咱们必须遵照的原则。欢迎园友拍砖斧正。若是园友们以为本文对你有帮助,请帮忙推荐,博主将继续努力~~