制作新手教学模块感悟梳理:
一、风险预判
(1)每个教学项中都一定会包含状态判断逻辑,以判断在该状态下应该走什么样的逻辑,在每一步逻辑进行之前都需要知道我的前置任务是否完成,按照这样的思路,不难发现如果教学步骤增多,一定会导致代码非常冗长,可维护性较差;
(2)其中一定会拥有较为复杂的逻辑判断,包含大量if...else...语句用于进行状态转换与前置后置条件的判断,代码测试难度较大,且不易于维护;
(3)系统扩展性较差,如果需要在已完成的教学中间会插入一段预选逻辑,首先需要完全理解整段逻辑的含义,并且需要对原有代码进行大量修改,扩展起来非常麻烦。
二、解决方案
为了解决这些问题,就想到了状态模式,在状态模式中,将对象在每个状态下的行为和状态转语句封装在一个个状态类中,通过这些状态类来分散冗长的条件转换语句,让系统具有更好的灵活性和可扩展性,状态模式可以在一定程度上解决上述问题。
该模块使用状态模式作为基本架构思路,状态模式是一种对象行为型模式。在状态模式中引入了流程管理类、抽象状态类、具体状态类。
当我在实际做新手教学的时候发现不同教学模块所使用的数据并不相同,如果按照此固定结构势必会导致两个问题:
①如果将单独状态中用到的数据统一提取到抽象状态类中的话,导致抽象状态类存储的数据暴增,并且在执行某一个教学期间,不属于这项教学的其他数据并没有被使用
②由于状态与状态之间是完全解耦的,如果每个单独状态独自管理自己所用的数据,这样又会导致同样的数据在各个状态类中重复获取,对于性能而言并不友好。
针对这两个痛点,我创新性的通过引入ITrainingState状态接口,针对同一教学的所有具体状态类,都继承自对应教学模块的Training Base抽象状态类,TrainingBase抽象状态类继承自ITrainingState状态接口。这样,对于同一教学部分的公用数据就可以统一提取到自己所继承的TrainingBase中,FSM_Manager统一管理ITrainingState对象,这样完美的解决了两个问题。我设计的代码结构如图所示:
●ITrainingState(状态接口):定义了流程管理者对于一个特定状态类的相关行为,包含进入状态,退出状态和状态进行中3种行为,具体逻辑在其子类中实现这些方法。项目中的典型代码逻辑如下:
public interface ITrainingState
{
void Enter();
void Update();
void Exit();
}
●FSM_Manager(流程管理类):它拥有当前教学模块包含的所有状态的对象。由于状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类,在管理者中维护一个状态接口类ITrainingState的实例,这个实例定义当前状态,在具体实现时,他是一个TrainingBase子类的对象。项目中的典型代码逻辑如下:
public class FSM_Manager
{
private ITrainingState currentState;
private Dictionary<NoviceTeachingStates, ITrainingState> rocketStates;
public void AddState(NoviceTeachingStates rs, ITrainingState ifs)
{
if (rocketStates == null)
rocketStates = new Dictionary<NoviceTeachingStates, ITrainingState>();
if (!rocketStates.ContainsKey(rs))
{
rocketStates.Add(rs, ifs);
}
}
/// <summary>
/// 切换状态
/// </summary>
public void TransitionState(NoviceTeachingStates nextState)
{
if (nextState == NoviceTeachingStates.None) return;
if (currentState != null)
{
currentState.Exit();
}
currentState = rocketStates[nextState];
currentState.Enter();
}
public void Update()
{
currentState?.Update();
}
public void EndState()
{
currentState?.Exit();
rocketStates.Clear();
rocketStates = null;
}
}
●TrainingBase(抽象状态类):它继承自ITrainingState状态接口,在抽象状态类中声明了状态对应的必要抽象方法和对应教学模块中包含的必要数据,而在其子类中实现这些抽象方法,由于不同状态下对象的行为不同,因此在不同子类中方法的实现可能存在不同,而同样的方法可以写在抽象状态类中。项目中典型逻辑如下:
public abstract class TrainingBase : ITrainingState
{
protected TainingControllerBase controller;
protected HNoviceTeachingData currentData;
protected NoviceTeachingStates nextState;
public TrainingBase(TainingControllerBase controller,HNoviceTeachingData data,NoviceTeachingStates nextState)
{
this.controller = controller;
this.currentData = data;
this.nextState = nextState;
}
protected void ShowMask(MaskType maskType,RectTransform mask=null, Vector3 targetPos=default)
{
HEventManager.Emit(HEventNames.NoviceMaskShowInfo, mask, targetPos, maskType);
}
protected void TransionNextState()
{
if (currentData.IsAutoNext=="1")
Next();
}
private void Next()
{
if (nextState == NoviceTeachingStates.None)
controller.OnEndTraining();
else
controller.fsm.TransitionState(nextState);
}
public void Enter()
{
HEventManager.AddListener(HEventNames.NoviceMaskClickNext,Next);
HEventManager.Emit(HEventNames.NoviceTipsInfo, currentData.Name, currentData.IsAutoNext == "0");
OnEnter();
}
public abstract void OnEnter();
public void Update()
{
OnUpdate();
}
public abstract void OnUpdate();
public void Exit()
{
HEventManager.RemoveListener(HEventNames.NoviceMaskClickNext,Next);
OnExit();
}
public abstract void OnExit();
}
●ConcreteState(具体状态类):它是抽象状态类的子类,每一个子类实现一个与当前教学子步骤数据相关的行为逻辑,每一个具体状态类对应教学中的一个具体步骤。项目中典型代码如下:
public class FirstView : TrainingBase
{
public override void OnEnter()
{
ShowMask(MaskType.FullScreen);
MonoManager.Instance.WaitTime(.5f, () => HServiceManager.GetService<HBattleCameraService>().CloseControl());
}
public override void OnUpdate()
{
}
public override void OnExit()
{
}
public FirstView(TainingControllerBase controller, HNoviceTeachingData data, NoviceTeachingStates nextState) :
base(controller, data, nextState)
{
}
}
三、开发总结
在实际教学步骤开发中,每一个状态都是可复用逻辑,并且随着开发更多的教学步骤,需要编写的步骤逻辑就会变少,此时一个非常复杂的流程性顺序逻辑,被改造成流水线式的组装逻辑,对新功能的开发和已有功能的改造都非常的简单。
并且通过对这部分业务的开发,我对项目中的技能释放流程,有了更深入的了解,包括可取消类的技能切换逻辑全面认识,还包括数据表的管理,对功能提取为表单的能力也有了很大提升。