前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >低耦合高内聚 原则的应用

低耦合高内聚 原则的应用

作者头像
twowinter
发布2020-04-17 15:05:33
9600
发布2020-04-17 15:05:33
举报
文章被收录于专栏:twowintertwowinter

这次主要是分享对软件设计中的“低耦合、高内聚”原则的一些个人体会,通过lorawan代码等实例分析,让大家对这个设计思想有一些具象的理解。

本文作者twowinter,转载请注明作者:http://blog.csdn.net/iotisan/

前言

“低耦合、高内聚”,乍听一下特别有逼格,瞬间让我们这次培训高大上了不少。

在一些设计模式的书籍,以及一些面向对象的书籍中,常常会看到这个词。设计模式主要是软件工程领域,特别是面向对象编程这个领域,大家原本都用一些很笨的办法在写代码,后面慢慢有一些人发现一些小技巧,可以让代码更易读、更易维护,再后来一些偏学术型的大牛,把一些常见的设计思路提炼提炼出来,就成了我们现在听到的设计模式。

因此今天要讲的耦合coupling,最早就是来自于面向对象编程。

那它是什么意思呢?

说下概念

作为一个正经的培训,我们来看看书中对它的定义:

耦合性:也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息 内聚性:又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高。

所谓高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则。

对于低耦合,粗浅的理解是:一个完整的系统,模块与模块之间,尽可能的使其独立存在。也就是说,让每个模块,尽可能的独立完成某个特定的子功能。模块与模块之间的接口,尽量的少而简单。如果某两个模块间的关系比较复杂的话,最好首先考虑进一步的模块划分。这样有利于修改和组合。

举个通俗的例子

这个例子是《大话设计模式》中介绍的,我觉得还挺有意思。

三国时期,(卧槽,三国时期? 嗯,三国时期),曹操在灭掉北方势力之后,带领百万大军攻打东吴,眼看就要灭掉东吴,统一天下,于是大宴众文武。酒席间,曹操诗兴大发,不觉吟道:“喝酒唱歌,人生真爽。……”。众文武齐呼:“丞相好诗!”于是一臣子速命印刷工匠刻板印刷,以便流传填下。 样张出来给曹操一看,曹操感觉不妥,说道:“喝与唱,此话过俗,应改为‘对酒当歌’较好!”,于是此臣就命工匠重新来过。工匠眼看连夜刻版之工,彻底白费,心中叫苦不喋。只得照办。 样张再次出来请曹操过目,曹操细细一品,觉得还是不好,说:“人生真爽太过直接,应改问语才够意境,因此应改为‘对酒当歌,人生几何?…………’!”当臣转告工匠之时,工匠卒…………

这里的刻板就是一个耦合体,所有的字都耦合在一块板子上。如果我们能单独对某个字进行修改,降低字与字之间的耦合,那就轻松多了。

喝酒唱歌,人生真爽。 对酒当歌,人生几何。

一大段的短歌行,这样只要改4个字。北宋的毕昇就是这样想的,于是活字印刷术诞生了。

怎么做

虽然说耦合性、内聚性是联系紧密程度的度量,但它是个挺虚的概念。我们只能想办法去尽量的实现“低耦合、高内聚”。

那究竟怎么做呢?

方法一 从总体结构上分解系统

这应该是最容易想到的办法,把复杂的系统“化整为零,各个击破”。功能上分解开了,一个模块实现一个独立的功能,自然就不耦合在一起了。

这在软件设计上,称之为 单一职责原则SRP(Single Responsibility Principle)。

比如我们早先的演示代码,就将各种业务功能与LoRa传输杂糅在一个模块里。

有一个我印象很深的例子,就是LoRaWAN的协议文档。原本协议框架及命令等,是和各个国家的地区参数一起发布的,后来由于LoRaWAN逐步应用过程中肯定会有很多新区域加进来,为了不影响旧有协议文档主体,所以从V1.0.2版本开始,联盟把地区参数这块内容单独出来。这就是一个解耦的例子。

方法二 从层级上提炼出抽象层

依赖倒转原则DIP(the Dependency Inversion Principle DIP),这个原则是 Martin, Robert C 在1996年提出来的。

A. High-level modules should not depend on low-level modules. Both should depend on abstractions. B. Abstractions should not depend on details. Details should depend on abstractions.

具体怎么讲呢?

这是传统的设计思路:

In conventional application architecture, lower-level components are designed to be consumed by higher-level components which enable increasingly complex systems to be built. In this composition, higher-level components depend directly upon lower-level components to achieve some task. This dependency upon lower-level components limits the reuse opportunities of the higher-level components.

这是DIP原则的实现方式:

DIP中提出了一个抽象接口。抽象接口是对低层模块的抽象,低层模块按要求来实现这个抽象接口。高层模块不直接依赖低层模块,而是依赖这个抽象接口。 所以,原本是高层依赖低层的情况,通过这个抽象接口操作,反而变成了低层模块要向上依赖这个抽象接口。这就是依赖倒转。

这样做的好处就是,高层模块和低层模块的耦合降低,高层模块的复用性极大的增强了。

事实上,这个思路大家应该都清楚。特别是我们嵌入式界,面对茫茫多的硬件设备,提炼出稳定的硬件抽象层,就显得特别重要。这样很多模块都可以复用。

这个方法在LoRaWAN协议栈中有一些运用。

关于这个Radio的用法,据我所知,物联网OS排行榜的第一名contiki也是这样定义的。

方法三 模块间尽量做到单向依赖

第三种方法,也是大家常会遇到的。

假设A是上层,B是它的下层,A依赖B。假如B也直接依赖A,那就可能造成循环依赖。比如说编译A模块时需要包含到B模块的文件,而编译B时同样要直接包含到A的文件。这种情况下,A和B的耦合就比较严重了。

单向依赖,就是说A模块可以调用B模块暴露的API,但B模块绝不允许调用A模块的API。

比如刚才提到的LoRaMac就有这种情况,当发数据时,是MAC传递给Radio,但接收数据时,是Radio传回给MAC。

这种情况下,就变成MAC和Radio循环依赖,这样子耦合就变得很严重。如果我们要换一个MAC,比如不走LoRaWAN的协议,那Radio中原来MAC的接口也要相应的变化。

我们最常见的办法是设置回调,这个例子中,MAC把接收函数以回调形式通过注册函数注入到Radio中,这样MAC还是依赖Radio的注册函数。

如下,MAC把接收函数注入到Radio中。

void LoRaMacInitialization(void)
{
    RadioEvents.TxDone = OnRadioTxDone;
    RadioEvents.RxDone = OnRadioRxDone;
    RadioEvents.RxError = OnRadioRxError;
    RadioEvents.TxTimeout = OnRadioTxTimeout;
    RadioEvents.RxTimeout = OnRadioRxTimeout;
    Radio.Init( &RadioEvents );
}

Radio 接收数据后,处理回调函数

void SX1276OnDio0Irq( void ) 
{
    if( ( RadioEvents != NULL ) && ( RadioEvents->RxDone != NULL ) )
    {
        RadioEvents->RxDone( RxTxBuffer, SX1276.Settings.FskPacketHandler.Size, SX1276.Settings.FskPacketHandler.RssiValue, 0 );
    }
}

总结

今天这篇分享,主要从“总体结构”->“系统层级”->“模块间”这三个从大到小的层面,分享了代码设计上解耦的一些思路。

希望对大家有所启发。


本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 说下概念
  • 举个通俗的例子
  • 怎么做
    • 方法一 从总体结构上分解系统
      • 方法二 从层级上提炼出抽象层
        • 方法三 模块间尽量做到单向依赖
        • 总结
        相关产品与服务
        腾讯物联网终端操作系统
        腾讯物联网终端操作系统(TencentOS tiny)是腾讯面向物联网领域开发的实时操作系统,具有低功耗,低资源占用,模块化,安全可靠等特点,可有效提升物联网终端产品开发效率。TencentOS tiny 提供精简的 RTOS 内核,内核组件可裁剪可配置,可快速移植到多种主流 MCU 及模组芯片上。而且,基于RTOS内核提供了丰富的物联网组件,内部集成主流物联网协议栈(如 CoAP/MQTT/TLS/DTLS/LoRaWAN/NB-IoT 等),可助力物联网终端设备及业务快速接入腾讯云物联网平台。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档