如何提高代码的扩展性(6)

简介: 如何提高代码的扩展性(6)

1、场景举例分析


二、场景示例
下面就以我们传统行业的订单操作为例来说明下接口隔离的必要性。
1、胖接口
软件设计最初,我们的想法是相同功能的方法放在同一个接口里面,如下,所有订单的操作都放在订单接口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接口里面增加了一个方法,销售订单的实现类是不是也必须要实现一个无效的接口方法?这就是我们常说的“胖接口”导致的问题。由于接口过“胖”,每一个实现类依赖了它们不需要的接口,使得层与层之间的耦合度增加,结果导致了不需要的接口发生变化时,实现类也不得不相应的发生改变。这里就凸显了我们接口隔离原则的必要性,下面我们就来看看如何通过接口隔离来解决上述问题。
2、接口隔离
我们将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("订单结束");
        }
    }


这样设计就能完美解决上述“胖接口”导致的问题,如果需要增加订单操作,只需要在对应的接口和实现类上面修改即可,这样就不存在依赖不需要接口的情况。通过这种设计,降低了单个接口的复杂度,使得接口的“内聚性”更高,“耦合性”更低。由此可以看出接口隔离原则的必要性。


另:

有称六大设计原则:+迪米特法则。


迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。


举例:明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如与粉丝的见面会,与媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则,其类图如图 所示。


image.png

相关文章
|
Python
项目依赖的python包requirements.txt文件的生成与安装
项目依赖的python包requirements.txt文件的生成与安装
1479 0
|
机器学习/深度学习 数据采集 人工智能
人工智能安全(下)
人工智能安全(下)
972 0
人工智能安全(下)
|
12月前
|
消息中间件 Kafka
【赵渝强老师】Kafka生产者的执行过程
Kafka生产者(Producer)将消息序列化后发送到指定主题的分区。整个过程由主线程和Sender线程协调完成。主线程创建KafkaProducer对象及ProducerRecord,经过拦截器、序列化器和分区器处理后,消息进入累加器。Sender线程负责从累加器获取消息并发送至KafkaBroker,Broker返回响应或错误信息,生产者根据反馈决定是否重发。视频和图片详细展示了这一流程。
283 61
|
消息中间件 监控 持续交付
《云消息队列RabbitMQ实践》解决方案测评报告
《云消息队列RabbitMQ实践》解决方案通过RabbitMQ实现业务解耦、异步处理和高可用性。其核心优势包括消息持久化、灵活路由及高可靠性。文档详细介绍了部署步骤、配置方法及监控手段,帮助用户快速搭建消息队列系统。方案适用于电商、金融和实时数据处理等高并发场景,通过异步处理提升系统性能。建议增加自动化部署、复杂场景示例及更详尽的日志解析,进一步提升用户体验。
|
移动开发 JavaScript 前端开发
简单易用的jquery响应式轮播图插件ma5slider
ma5slider是一款简单易用的jquery响应式轮播图插件。该轮播图支持鼠标拖拽,可以通过CSS定制外观,支持无限循环模式,内置水平,垂直和淡入淡出三种轮播图过渡动画效果。
|
并行计算 前端开发 物联网
全网首发!真·从0到1!万字长文带你入门Qwen2.5-Coder——介绍、体验、本地部署及简单微调
2024年11月12日,阿里云通义大模型团队正式开源通义千问代码模型全系列,包括6款Qwen2.5-Coder模型,每个规模包含Base和Instruct两个版本。其中32B尺寸的旗舰代码模型在多项基准评测中取得开源最佳成绩,成为全球最强开源代码模型,多项关键能力超越GPT-4o。Qwen2.5-Coder具备强大、多样和实用等优点,通过持续训练,结合源代码、文本代码混合数据及合成数据,显著提升了代码生成、推理和修复等核心任务的性能。此外,该模型还支持多种编程语言,并在人类偏好对齐方面表现出色。本文为周周的奇妙编程原创,阿里云社区首发,未经同意不得转载。
30808 18
|
Java Android开发
MAT如何使用?
MAT如何使用?
1028 4
|
Ubuntu Linux Python
Ubuntu学习笔记(六):ubuntu切换Anaconda和系统自带Python
本文介绍了在Ubuntu系统中切换Anaconda和系统自带Python的方法。方法1涉及编辑~/.bashrc和/etc/profile文件,更新Anaconda的路径。方法2提供了详细的步骤指导,帮助用户在Anaconda和系统自带Python之间进行切换。
627 1
|
前端开发 JavaScript 数据可视化
Webpack加载器和插件之间有什么区别
【10月更文挑战第13天】Webpack加载器和插件之间有什么区别
|
网络协议
思科交换机输入错误命令卡死
思科交换机输入错误命令卡死
569 0