1. 什么是耦合?有什么坏处?

在这里插入图片描述
藕这种东西大家都听说过吧?
耦合就是像耦一样的东西…藕断…嘶…连

短路3s…

我们修改A模块,结果导致B崩溃了,修改B模块导致C模块崩溃了,至此996熬夜加班,万古如长夜

如果A,B,C之间半点关系没有,那是不是可以哪里坏了点哪里!!哪里坏了修哪里,即使我不断反向修复,那么坏的也最多一个模块,而且,要是我们把不同的功能分开放,也方便改不是?

我们在编写代码时应该考虑到以后可能的修改需求,有良好的拓展性,但是我们不期待完全解耦,这是不现实的,过于严重的解耦会导致代码割裂而散乱,我们追求”低耦合,高内聚“

耦合性:也称块间联系。指软件系统结构中各模度块间相互联系紧密程度的一种度量。模块之问间联系越紧密,其耦合性就越答强,模块的独立性则越差。

来自 https://blog.csdn.net/qq_39736982/article/details/81478176这里是引用

2. 如何做到解耦合?

打个(不)恰当的比喻就像是一对热恋的情侣…十分耦合…咳咳…我这是在讨论技术…(我朋友爱看,呸,看什么,这是技术文)…
如何解耦合…就是如何逐步降低他们的关系(狗头护体,滑稽护体)
显然是让他们不再相互依赖!
两个模块之间降低依赖程度,就是耦合度低了
但是依赖是不可避免的,就像人与人总是有联系。

没有人是与世隔绝的孤岛,每个人都是广袤大陆的一部分

我们直接没有太多直接关联,笔者不会向你收钱,你的钱也不是我的(哭)

也就是说,笔者发几篇文章也是发在CSDN而不是直接发给读者,读者想看也只能通过CSDN而不是找笔者要。

我们的直接联系就变少了,通过一个类似的中介者也可以让代码耦合变少(中介模式)

3. 简易消息机制解耦合

来到了本文核心内容,消息机制
那么请思考,如何创建这个中介?
还是上文那对小情侣…他们通过微信联系,那么微信发的就是消息

男: 亲爱的,星期日 我们森林公园
女: 好啊!不见不散

那么继续思考,谁是中介? 当然是微信了,我们要创造一个类似微信,CSDN的中介,也就是专门打造一个平台,负责传递消息
女生只要想去公园玩,女生就会通过微信去联系男生,而非直接去找男生。

穿过多啦A梦的任意门回到文章内…

首先,我们要清楚实现什么

1. 微信(中介平台)
2. 微信消息(包含时间,地点,动作,对象)
在这里插入图片描述

只要中介者不崩溃,A出问题和B没关系,B坏了和A也没关系,这不就降低了耦合度

给消息命名

先从微信开始

消息辣莫多,我怎么知道哪条是哪条?
怎么区分是哪条消息?我们应该给每条消息加一个标识符。

可以用字符串或者枚举甚至int等多种类型作为标识符
各有优点,字符串是方便,但是字符串性能不是很理想,因为比对一个10长度的字符串就是比对10个char,远低于比对一个int。

int会没有描述性,123456789,请找到跳跃对应的事件…明显找不到嘛
枚举呢,每次都需要手动配置,但是兼顾的性能和描述性,也不是很方便。
虽然很不愿意,但是的确很难找到更合适的消息标识了。

我使用了枚举标记一类事件,需要增加新的事件就添加枚举

public enum MessageList
    {
        Meet //约会的标志,这个消息叫Meet
    }

(2)约定消息格式

我们怎么约定消息的格式?意思就是模块间消息只能这么写,毕竟是写给电脑看的,电脑不像人一样有着智能,它只能读懂固定格式的语言
不同模块直接传递的消息,有时候要有参数,一般也就4个以内,好在C#为开发者提供了泛型,可以自行决定参数类型

namespace xxx
{
    public delegate void Message();
    public delegate void Message<T>(T prarm1);
    public delegate void Message<T, P>(T prarm1, P param2);
    public delegate void Message<T, P, W>(T prarm1, P param2, W param3);
    public delegate void Message<T, P, W, G>(T prarm1, P param2, W param3, G param4);
}

读者可能会在网上看到很多使用object[]和parm的消息机制,最大的问题就是在值类型转object时产生装箱,所以这里采用泛型规避这个问题,缺点就是代码变得难以维护,因为不只是委托需要使用大量泛型,注册和取消注册,执行都需要。

(3)模拟消息中心(中介者)

那我们把消息存在哪里,当然是在中介里构造一个数据库
引入一个概念,叫 容器
什么是容器,就像微信的数据库,容纳所有用户的消息.
键就是消息的名字,这样消息就有唯一性了
中介者微信怎么实现,我们用一个类模拟,在其中创建静态Dictionary来存储消息。

    public static class MessageCenter 
    {
    	//这个类是消息中心,就是我们的微信
    	.
		private static Dictionary<MessageList, Delegate> MessagePool = new Dictionary<MessageList, Delegate>();//这就是我们的数据库,存消息
		
	}

添加一个用来添加消息的方法

//添加到消息中心存储
public static void Add(MessageList messageType, Message message)//传入一个枚举标记这个事件
{
	//这里采用switch只是为了让读者更明确分支结构,属于故意而为。
	//实际上双分支并不需要采用switch
	switch (MessagePool.ContainsKey(messageType))   //判断容器里有没有这个类型的事件
	{
		case true:    //如果有
		{
			MessagePool[messageType] = (Message)MessagePool[messageType] + message;    //委托中加入一个新的方法
			break;
		}
		case false:   //如果无
		{
			MessagePool.Add(messageType, message);     //添加一个新的委托
			break;
		}
	}
}

声明一个方法,用来判断是否存在这个事件,并且是不是空

private static bool IsInDic(MessageList messageType)   //传入枚举标志
{
	//这里两个if可以写在一起,分开写让读者看的更清楚
     if (MessagePool.ContainsKey(messageType))  //如果有这个事件
     {
          if (MessagePool[messageType] != null)  //如果这个事件不是空的
          {
               return true;  //返回真
          }
      }
      return false; 
}

有消息,自然需要执行

public static void Invoke(MessageList eventType)    //传入消息类型
        {
            if (IsInDic(messageType))//非空
            {
                Message msg = MessagePool[messageType] as Message;   //拿出来这个事件
                msg?.Invoke();   //不空就执行他...
            }
        }

自然也需要移除

public static void Remove(MessageList messageType, Message message)
        {
            if (MessagePool.ContainsKey(messageType))
            {
                MessagePool[messageType] = (Message)MessagePool[messageType] - message;

                if (MessagePool[messageType] == null)
                {
                    MessagePool.Remove(messageType);
                }
            }

        }

以上几部分在一起就是消息中心的部分代码

消息机制三要素
1.注册Add
2.移除注册Remove
3.发送消息Invoke

(4) 使用

整个过程写的很清楚了,这就是约定一个事件
如何约定一个像上文提到的事件呢

//假设这个函数是入口函数
void Achievement()
{
	//两个约定
	MessageCenter.Add(MessageList.Meet,和男1去公园玩);
	MessageCenter.Add(MessageList.Meet,和男2去公园玩);
	//取消一个
	MessageCenter.Remove(MessageList.Meet,和男2公园玩);
	//执行约会
	MessageCenter.Invoke(MessageList.Meet);
}
void 和男1去公园玩()
{
	Debug.Log("女主要和男1出去玩了");
}
void 和男2去公园玩()
{
	Debug.Log("女主要和男2出去玩了");
}

到这里读者可能会觉得,这仅仅只是在一个方法里执行另外两个方法,有什么用?
笔者想说,如果Achievement方法和这两个方法分别处于两个不同的复杂结构中,我们难以直接直接调用这两个方法,而强行调用会让结构非常混乱,所以我们才建立这样的消息机制来应对,更多的细节更需要读者在实践中慢慢体会。
下一篇文章是笔者在大一时写的填坑笔记对这篇文章进行补充

以上举例只是玩笑,愿天下有情人终成眷属。

感谢看完,小漫画加深感悟,通过中介者模式实现模块之间解耦,但是每个模块都与消息中心耦合。
在这里插入图片描述
在这里插入图片描述