Delegate的本质是回调,是观察者模式的应用。
A里产生变化需要与A1,A2,A3有交互。有3种方式:
方式1:A里调用A1, A2,A3的方法。
方式2:A1, A2, A3里调用A的方法。
方式3:弄一个中间者,Event/Delegate,A不用知晓A1, A2,A3的存在,A1,A2,A3也不用知道A的存在(Event/Delegate可以不用放在A里,另外放在另一个类里也没问题)。 A1, A2,A3里往Event/Delegate里添加回调函数。
方式1和方式2写得比较随意简单,增加了耦合度。但对于代码中很少用到的函数调用,且本身A1,A2,A3已经有了依赖于A的耦合度了,不妨直接调用,简单有效。
方式3的写法比较正规,可以完全解耦。
举例说明:
AudioManager里提供了接口:
void PreLoadBankByWeapon(int32 WeaponID)
void UnLoadBankByWeapon(int32 WeaponID)
因为AudioManager相对而言更底层,不会依赖上层的WeaponSys。 WeaponSys可以依赖AudioManager,毕竟需要调用AudioManager提供的PostEvent(***)等音频播放函数。
因此业务系统调用这两个接口有可选的两种方式:
1) WeaponSys里在需要的地方直接调用。
2) WeaponSys在需要的地方抛出Event/Delegate, AudioManager往Event/Delegate里注册这两个接口函数作为回调。
这两个方式都可以,看业务系统觉得怎么方便吧。负责WeaponSys的同事觉得直接调用AudiaoManager里的函数更方便,免去了加Event/Delegate的麻烦。 负责任务系统的同事则不喜欢直接调用,加的Event/Delegate有通用性,除了给AudiaoManager使用还可以给其他有需要的使用。
理解Delegate/Event,只需要把握3点:
1. Delegate/Event可绑定多个函数还是单个函数。
2. Delegate/Event的触发。
3. Delegate/Event回调函数的绑定。
看UE4里的Delegate/Event,则看3点:
1. Delegate/Event的声明。
UE4里Delegate/Event的声明,偏重于3点:
1) 单播 vs 多播 MULTICAST 带有"MULTICAST"的为多播,无则为单播。
2) 静态 vs 动态 DYNAMIC DYNAMIC意味着会添加到反射系统,可以供蓝图使用供Lua使用。
3) 是否有参数。
2. Delegate/Event的触发。
UE4里的Event其实是受限的Delegate,Event只能由自身触发,普通的Delegate无此限制。
单播的触发用Execute(***) ExecuteIfBound(***), 若Delegate/Event里有参数则参数带上。单播需要判断IsBound()。
多播的触发使用Broadcast(***), 若Delegate/Event里有参数则参数带上。多播无需判断IsBound()。
3. Delegate/Event回调函数的绑定。
静态动态 单播多播 2*2, 4种排列组合。
静态单播
//声明
DECLARE_DELEGATE_OneParam(MyIntDelegate, int32)
MyIntDelegate IntDelegate;
//绑定 --BindUObject与BindUFunction均可,更推荐BindUObject
IntDelegate.BindUFunction(this, FName("IntFunction"));
IntDelegate.BindUObject(this, &UTheClass::IntFunction); //更推荐
//调用
IntDelegate.Execute(999); // or IntDelegate.ExecuteIfBound(999);
静态多播
//声明
DECLARE_MULTICAST_DELEGATE_OneParam(MyIntDelegate, int32)
MyIntMulticastDelegate IntMulticastDelegate;
//绑定 --AddUObject与AddUFunction均可,更推荐AddUObject
IntMulticastDelegate.AddUFunction(this, FName("IntFunction"));
IntMulticastDelegate.AddUFunction(this, FName("SecondIntFunction"));
IntMulticastDelegate.AddUFunction(this, FName("ThirdIntFunction"));
IntDelegate.AddUObject(this, &UTheClass::IntFunction); //更推荐
//调用
IntMulticastDelegate.Broadcast(123);
动态单播
//声明
DECLARE_DYNAMIC_DELEGATE_TwoParams(FMyIntDel, int32, iCount);
//绑定 --只有BindDynamic
MyIntDel.BindDynamic(this, &UTheClass::IntFunction)
//调用
MyIntDel.ExecuteIfBound(123);
动态多播
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FMyDel, int32, iCount, float, fHeight);
//绑定 --只有AddDynamic
MyDel.AddDynamic(this, &UTheClass::MyFunction)
//调用
MyDel.Broadcast(123, 3.14f);
完整的示例
//TDHintSys.h
//静态单播
DECLARE_DELEGATE_OneParam(FMySingleDelegate, int32);
//动态单播
DECLARE_DYNAMIC_DELEGATE_OneParam(FMySingleDynamicDelegate, int32, iYearCount);
//静态多播
DECLARE_MULTICAST_DELEGATE_TwoParams(FMyMultiDelegate, int32, float);
//动态多播
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FMyMultiDynamicDelegate, int32, iYearCount, float, fHeight);
UFUNCTION(Exec)void IvyTestDel(int32 arg);
void OnMySingle(int32 val);
UFUNCTION() void OnMySingleUFunc(int32 val);
void OnMySingleDynamic(int32 iYearCount);
UFUNCTION() void OnMySingleDynamicUFunc(int32 iYearCount);
void OnMyMulti(int32 iYearCount, float fHeight);
UFUNCTION() void OnMyMultiUFunc(int32 iYearCount, float fHeight);
void OnMyMultiDynamic(int32 iYearCount, float fHeight);
UFUNCTION() void OnMyMultiDynamicUFunc(int32 iYearCount, float fHeight);
//TDHintSys.cpp
void UTDHintSys::IvyTestDel(int32 arg)
{
switch(arg)
{
case 1:
{
FMySingleDelegate Del;
Del.BindUObject(this, &UTDHintSys::OnMySingle);
Del.ExecuteIfBound(arg);
break;
}
case 2:
{
FMySingleDelegate Del;
Del.BindUObject(this, &UTDHintSys::OnMySingleUFunc);
Del.ExecuteIfBound(arg);
break;
}
case 3:
{
FMySingleDelegate Del;
Del.BindUFunction(this, FName("OnMySingle")); //cause to crash
Del.ExecuteIfBound(arg);
break;
}
case 4:
{
FMySingleDelegate Del;
Del.BindUFunction(this, FName("OnMySingleUFunc"));
Del.ExecuteIfBound(arg);
break;
}
case 11:
{
FMyMultiDelegate Del;
Del.AddUObject(this, &UTDHintSys::OnMyMulti);
Del.Broadcast(arg, 3.14f);
break;
}
case 12:
{
FMyMultiDelegate Del;
Del.AddUObject(this, &UTDHintSys::OnMyMultiUFunc);
Del.Broadcast(arg, 3.14f);
break;
}
case 13:
{
FMyMultiDelegate Del;
Del.AddUFunction(this, FName("OnMyMulti")); //cause to crash
Del.Broadcast(arg, 3.14f);
break;
}
case 14:
{
FMyMultiDelegate Del;
Del.AddUFunction(this, FName("OnMyMultiUFunc"));
Del.Broadcast(arg, 3.14f);
break;
}
case 21:
{
FMyMultiDynamicDelegate Del;
Del.AddDynamic(this, &UTDHintSys::OnMyMultiDynamicUFunc);
Del.Broadcast(arg, 3.14f);
break;
}
case 22:
{
FMyMultiDynamicDelegate Del;
Del.AddDynamic(this, &UTDHintSys::OnMyMultiDynamic); //cause to crash
Del.Broadcast(arg, 3.14f);
break;
}
case 31:
{
FMySingleDynamicDelegate Del;
Del.BindDynamic(this, &UTDHintSys::OnMySingleDynamicUFunc);
Del.ExecuteIfBound(arg);
break;
}
case 32:
{
FMySingleDynamicDelegate Del;
Del.BindDynamic(this, &UTDHintSys::OnMySingleDynamic);//cause to crash
Del.ExecuteIfBound(arg);
break;
}
default:
break;
}
}