耦合与解耦

 

高内聚,低耦合是你们我一直所追求的目标

前言

本人只是个菜鸡,只是写一些自己对于架构的一些小小的理解,可能会出偏差

何为耦合

耦合性(英语:Coupling,dependency。或称耦合力或耦合度)是一种软件工程的度量,是指一程序中,模块及模块之间信息或参数依赖的程度。

内聚性是一个和耦合性相对的概念,一般而言低耦合性代表高内聚性,反之亦然。耦合性和内聚性都是由提出结构化设计概念的赖瑞·康斯坦丁所提出。低耦合性是结构良好程序的特性,低耦合性程序的可读性及可维护性会比较好。

看到了上面的那句话,可能会感觉概念还是有所模糊。我们毕竟是写代码的不是写文章的嘛,也就不需要说那么多没什么用的废话了。

也就是说我们的一个程序中几个不同基本模块(可能是函数、类或者更小的基本单位)中存在着互相调用或者互相依赖的行为。

耦合

耦合的例子

例子1

1
2
3
4
5
6
7
8
void change(const vector<int>& source)
{
GetTool("H_1")->add(source[1]);
if (source[1] > 10)
GetTool("A_0")->change(source[1]);
if (source[1] < 10)
GetTool("A_1")->change(source[1]);
}

举这一个并不极端的例子,是因为我编不出来了这是我平时碰到的真实的问题。当我写完代码是又回头看了一眼,发现。

我*

他妈的应该是source[2]…于是我一一把他们从1改为2。也许你们觉得这也没什么大不了的,不就是一一替换嘛,有什么大不了的。但是如果我给你个500行的复合模块,然后你把source[2]写成了source[1],而且在函数的正确的逻辑中本来就有source[1]。那我们就不能简单的替换,而需要从头审逻辑,再改。甚至可能需要重写,这个代价就太大了。

这就是耦合的一种,过度(自己体会)使用了其他的模块的数据

例子2

举个比较zz的例子

1
2
3
xxx_0 = xxx_0 * 11 + 1;
xxx = ToList(xxx_0);
GetTool("IDX")->add(xxx);

这样的代码大家一定经常用到。有些人(),经常性的将这些段落直接复制粘贴来来去去。最后在改动的时候,就需要全篇的瞎瘠薄乱改,最后浪费了巨量的时间。

例子3

比方说我们有菜单类和按钮类。而按钮显然是在菜单里的。

我们现在是把按钮写死在菜单里好呢,还是各自独立好呢。

显然的,自然是同为独立类,再讲按键映射到菜单里好。因为菜单和按钮显然不是固定的关系,一个菜单拥有不定数量的按钮。如果写在一起的话,其实是很僵化的,换言之,耦合度太高,不利于维护和扩展。我们应该在菜单里以引用表的形式将按钮组合进去

例子4

想必大家对于java的set、get这个段子,应该有所耳熟,可是为什么这么写呢。

主要是为了解耦。我呢,架构能力很差,就引用大佬的话来说说吧。

很多人,并不是面向对象学得不好,但总觉得差什么,我也经历过,面向对象,封装,多态,继承,学过的人都知道,都认为自己了解了,其实不然,很多很奇妙的因素,让你误解了,大部分的人认为封装很简单,其实大错特错了,封装是最奇妙的,也是最难用好的。只要你记住以下原则,必然能很好地用好封装,成员尽量使用protected和private,不要去使用public.尽量不要提供给外部对成员属性getter的接口,意思就是不要暴露成员,为什么要这样呢?很简单,暴露成员属性必然会导致自身业务的外泄,业务外泄,会导致,类之间的无谓耦合,如A类有成员a,而程序需要对a数据改变,而你提供一个B类可以访问a成员的getter接口,B类在其自身对a修改,看上去没什么,实际上,就是类耦合,对a的修改是类A的职务,由于习惯的提供getter,导致了,在写类B的时候错误地添加了修改业务,使类A内聚能力降低,程序逐步庞大必然会越发明显,真所谓牵一发动全身,小程序确实是很难看出问题所在。

如何避免耦合

我呢水平一般,只能提出一点点微小的建议

  • 大的函数如果有可能只用到小的功能时,一定要把它分割。
  • 如果重复的一部分反复用到的话,需要注意把它单独拿出来。
  • 注意封装与设计模式。

具体实现方法

part 1

先看一段代码

1
2
3
4
5
6
7
8
9
10
11
12
void Set(MODE mode)
{
switch (mode)
{
case A:
...
case B:
...
default:
assert(false);
}
}

这是一个实际程序中的设置方法,其中MODE是一个枚举类。根据解耦的思想应该改为。
void SetA(…)

void SetB(…)


void Set(MODE mode)
{
switch (mode)
{
case A:
SetA(…);
case B:
SetB(…);
default:
assert(false);
}
}

part 2

1
2
3
4
5
6
config_add = None
def SetConfigAdd():
global config_add
config_add = xxx
def PushSetToFile():
# use config_add to do sth

像是这样的代码就不大好,全局的设置变量是一种相当不安全、不专业的行为(但是我很喜欢啊)。

应该在需要出现config_add的最大作用域(不超过主函数),声明config_add,把它作为引用或者常引用映射进去。(特指某些语言)

后记

架构设计对于程序员要求非常高。我只学了一些皮毛,甚至有些方法都只是我自己想的。所以只做参考吧。

学习!学习!