如何提高代码的扩展性(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文件的生成与安装
1838 0
|
消息中间件 监控 持续交付
《云消息队列RabbitMQ实践》解决方案测评报告
《云消息队列RabbitMQ实践》解决方案通过RabbitMQ实现业务解耦、异步处理和高可用性。其核心优势包括消息持久化、灵活路由及高可靠性。文档详细介绍了部署步骤、配置方法及监控手段,帮助用户快速搭建消息队列系统。方案适用于电商、金融和实时数据处理等高并发场景,通过异步处理提升系统性能。建议增加自动化部署、复杂场景示例及更详尽的日志解析,进一步提升用户体验。
|
SQL 运维 监控
MSSQL性能调优实战技巧:索引优化、SQL查询优化与并发控制策略
在Microsoft SQL Server(MSSQL)的运维过程中,性能调优是确保数据库高效运行、满足业务需求的关键环节
|
存储 SQL 边缘计算
Gartner APM 魔力象限技术解读——全量存储? No! 按需存储?YES!
在云原生时代,充分利用边缘节点的计算和存储能力,结合冷热数据分离实现高性价比的数据价值探索已经逐渐成为 APM 领域的主流。
3082 89
Gartner APM 魔力象限技术解读——全量存储? No! 按需存储?YES!
|
监控 Serverless 数据处理
函数计算产品使用问题之如何解决配置模型管理的自定义域名后无法访问
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
228 0
|
数据可视化 数据挖掘
K-means和层次聚类分析癌细胞系微阵列数据和树状图可视化比较
K-means和层次聚类分析癌细胞系微阵列数据和树状图可视化比较
K-means和层次聚类分析癌细胞系微阵列数据和树状图可视化比较
|
存储 负载均衡 安全
百度搜索:蓝易云【聊聊ConcurrentHashMap的存储流程】
通过以上分段锁和哈希表的设计,ConcurrentHashMap实现了高效的并发操作,使得多线程环境下的插入和获取元素操作不会造成线程间的竞争,从而提高了性能和并发能力。
180 1
|
Dubbo IDE 测试技术
探索 Seata 项目开源开发之旅
在本文中,作者将与大家分享我在 Seata 社区中的开发者之旅,以及在这个旅程中积累的经验和见解。希望通过我的故事,能够激励更多人踏上这充满挑战和激励的开源之路,为开源社区的繁荣做出自己的贡献。
|
Prometheus Kubernetes Cloud Native
Kubernetes Argo Rollouts 高级的部署能力
Kubernetes Argo Rollouts 高级的部署能力
904 1

热门文章

最新文章