上篇文章 已经讲了面向过程思想的重要性。
然而,最后的 lua 例子有些问题,连续的异步任务,很容易出现回调地狱。
一、解决方案
为了解决这个问题。 NRatel将 “一个异步过程” 概念抽象一下。将它称做 “步”(Step)。
对于每一“步”,都有一个进入方法和完成回调。
(因为是回调完成的方式,所以内部是同步完成的还是异步完成的,都无所谓)。
using System;
namespace NRatel
{
public interface IStep
{
void Enter();
void Enter(Action onFinish);
}
}
然后,在引申出一个 “多步” 的概念,与 “单步” 相比,它在进入后会进行进度反馈。
注意:“多步” 从整体来看也属于一个 “单步”。(多步继承自单步)。
using System;
namespace NRatel
{
public interface IMultiStep : IStep
{
void Enter(Action<int> onProgress, Action onFinish); //onProgres 范围(1~总步数)
}
}
------------------------- NRatel割 -------------------------
接下来,就可以实现一个可执行 List<IStep> 的、通用可嵌套的 “串行步进” 系统了。
需要注意:
1、由于List是引用传递,所以构造后不可对stepes进行增删操作,否则会导致堆栈溢出等问题。
2、这里暂时先不考虑从内部/外部打断多步过程的方法(很容易实现,但在深层嵌套时,可能需要自己处理打断的情况)。
using System;
using System.Collections.Generic;
namespace NRatel
{
/// <summary>
/// 串行步进(单元: IStp)
/// </summary>
sealed public class SerialSteps : IMultiStep
{
private readonly List<IStep> m_Stepes;
private Action<int> m_OnProgress;
private Action m_OnComplete;
private int m_Progress;
public SerialSteps(List<IStep> stepes)
{
this.m_Stepes = stepes;
}
public void Enter()
{
Enter(null, null);
}
public void Enter(Action onComplete)
{
Enter(null, onComplete);
}
public void Enter(Action<int> onProgress, Action onComplete)
{
m_Progress = 0;
m_OnProgress = onProgress;
m_OnComplete = onComplete;
Excute();
}
private void Excute()
{
if (m_Progress >= m_Stepes.Count)
{
if (m_OnComplete != null) { m_OnComplete(); }
return;
}
m_Stepes[m_Progress].Enter(Next);
}
private void Next()
{
m_Progress += 1;
if (m_OnProgress != null) { m_OnProgress(m_Progress); }
Excute();
}
}
}
二、应用一下
这里简单假设:要播一个战斗战报,它有m轮,每轮要播放n个动作。
这是一个自顶向下嵌套“过程”的例子。
using System;
using System.Collections.Generic;
using UnityEngine;
using NRatel;
namespace NRatel
{
public class BattleRepoert
{
private const int m = 3;
public void PlayRepoert()
{
Debug.Log("<color=red>----- PlayRepoert Start -----</color>");
List<IStep> steps = new List<IStep>();
for (int i = 0; i < m; i++)
{
steps.Add(new BattleRound());
}
new SerialSteps(steps).Enter(() =>
{
Debug.Log("<color=red>----- PlayRepoert Finish -----</color>");
});
}
}
public class BattleRound : IStep
{
private const int n = 2;
public void Enter() { PlayRound(null); }
public void Enter(Action onFinish)
{
PlayRound(onFinish);
}
private void PlayRound(Action onFinish)
{
Debug.Log("<color=green>### PlayRound Start ###</color>");
List<IStep> steps = new List<IStep>();
for (int i = 0; i < n; i++)
{
steps.Add(new BattleAction());
}
new SerialSteps(steps).Enter(() =>
{
Debug.Log("<color=green>### PlayRound Finish ###</color>");
onFinish?.Invoke();
});
}
}
public class BattleAction : IStep
{
public void Enter() { PlayAction(null); }
public void Enter(Action onFinish)
{
PlayAction(onFinish);
}
private void PlayAction(Action onFinish)
{
Debug.Log("@@@ PlayAction @@@");
onFinish?.Invoke();
}
}
}
public class TestSerialSteps : MonoBehaviour
{
private void Start()
{
Debug.Log("NRatel TestSerialSteps: ");
new BattleRepoert().PlayRepoert();
}
}
运行结果:
三、UML类图分析:
1、对于串行步进系统 SerialSteps。
可以看到它是一个自己组合自己抽象接口的类。这是一个明确的可嵌套的特征。
2、对于上面的例子。
可以看到,BattleRepoert 通过关联一个 SerialSteps ,组合了m个 BattleRound。
BattleRound 又通过关联一个 SerialSteps ,组合了m个 BattleAction。