对象之间的交互与模块化

对象之间的交互一般有三种

  • 方法调用,例如:A调用B的方法
  • 委托或者回调,例如:界面监听子按钮的点击事件
  • 消息或事件,例如:服务器向客户端发送通知(这里将Task和Async也包含在内)

模块化一般有三种

  • 单例,Manger Of Managers
  • IOC,例如,Extenject、uFrame的Container、StrangelIOC的绑定等等
  • 分层,例如:MVC、三层架构、领域驱动分层等等

模块化还有Entity Component(Unity的模块)、门面模式等等,这里只介绍以上三种

“点点点”项目的树结构

点点点项目

GameStartPanel脚本,触发(广播) GameStartEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace FrameWorkDesign.Example
{
public class GameStartPanel : MonoBehaviour
{
void Start()
{
transform.Find("Button_Start").GetComponent<Button>()
.onClick.AddListener(() =>
{
gameObject.SetActive(false);
GameStartEvent.Trigger();
});
}
}
}

父子节点的共识是:父节点可以引用子节点,子节点不能引用父节点。GameStartPanel脚本给子级的Button添加监听,子节点要调用父节点的方法时,可以用事件或委托

GameStartPanel的父节点是UI,Enemies的父节点是Game,说明GameStartPanel和Enemies是不同业务类型的对象,而不同业务模块之间对象的交互,叫做跨模块的对象交互,需要通过引用事件,来满足跨模块的交互需求

GameStartEvent脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;

namespace FrameWorkDesign.Example
{
public static class GameStartEvent
{
private static Action m_OnEvent;

public static void Register(Action onEvent)
{
m_OnEvent += onEvent;
}

public static void Unregister(Action onEvent)
{
m_OnEvent -= onEvent;
}

public static void Trigger()
{
m_OnEvent?.Invoke();
}
}
}

GameEndEvent脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;

namespace FrameWorkDesign.Example
{
public static class GameEndEvent
{
private static Action m_OnEvent;

public static void Register(Action onEvent)
{
m_OnEvent += onEvent;
}

public static void Unregister(Action onEvent)
{
m_OnEvent -= onEvent;
}

public static void Trigger()
{
m_OnEvent?.Invoke();
}
}
}

Enemy脚本,触发(广播)GameEndEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using UnityEngine;

namespace FrameWorkDesign.Example
{
public class Enemy : MonoBehaviour
{
static int count;
private void OnMouseDown()
{
count++;
if(count == 9)
{
GameEndEvent.Trigger();
}
Destroy(gameObject);
}
}
}

GameRoot脚本,它的作用是给GameStartEvent添加监听(注册),游戏启动时激活Enemies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEngine;

namespace FrameWorkDesign.Example
{
public class GameRoot : MonoBehaviour
{
private void Awake()
{
GameStartEvent.Register(OnGameStart);
}

private void OnGameStart()
{
transform.Find("Enemies").gameObject.SetActive(true);
}

void OnDestroy()
{
GameStartEvent.Unregister(OnGameStart);
}
}
}

UIRoot脚本,它的作用是给GameEndEvent添加监听(注册),游戏结束时激活Panel_GamePass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEngine;

namespace FrameWorkDesign.Example
{
public class UIRoot : MonoBehaviour
{
void Start()
{
GameEndEvent.Register(OnGameEnd);
}

private void OnGameEnd()
{
transform.Find("Canvas/Panel_GamePass").gameObject.SetActive(true);
}

void OnDestroy()
{
GameEndEvent.Unregister(OnGameEnd);
}
}
}

总结

  1. 子节点通知父节点用委托或事件
  2. 父节点调用子节点可以直接方法调用
  3. 跨模块通信用事件
  4. 耦合就是双向引用或循环引用

对象之间的交互的引用关系

  • 方法调用
    • A需要持有B才能调用B的方法
  • 委托 & 回调
    • A需要持有B才能注册B的委托
  • 消息 & 事件
    • 引入事件相当于引入第三方,A不需要持有B