状态模式代码原型
解决的问题:
主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化。
模式中的角色
1 上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。
2 抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
3 具体状态(Concrete State):实现抽象状态定义的接口。
状态模式原型代码的实现:
using UnityEngine;
public class StateDesi : MonoBehaviour
{
private void Start()
{
Context context = new Context();
context.SetState(new ConcreteStateA(context));
context.ContextHandle(5);
context.ContextHandle(20);
context.ContextHandle(30);
context.ContextHandle(4);
}
}
//状态的拥有者
public class Context
{
private IState mState;
public void SetState(IState state)
{
mState = state;
}
public void ContextHandle( int arg)
{
mState.Handle(arg);
}
}
public interface IState
{
//处理当前状态的行为
void Handle(int arg);
}
public class ConcreteStateA : IState
{
private Context mContext;
public ConcreteStateA(Context context)
{
mContext = context;
}
public void Handle(int arg)
{
Debug.Log("ConcreteStateA.Handle"+arg);
if (arg > 10)
{
mContext.SetState(new ConcreteStateB(mContext));
}
}
}
public class ConcreteStateB : IState
{
private Context mContext;
public ConcreteStateB(Context context)
{
mContext = context;
}
public void Handle(int arg)
{
Debug.Log("ConcreteStateB.Handle" + arg);
if (arg <= 10)
{
mContext.SetState(new ConcreteStateA(mContext));
}
}
}
输出结果:
首先设置的初始状态是ConcreteStateA,然后然后判断arg是否大于10,如果大于10就切换到ConcreteStateB状态,到达ConcreteStateB状态后每次有判断arg是否小于等于10,如果小于等于又切换到ConcreteStateA状态,这就是状态直接的相互切换。
场景状态跳转栗子
下面我举例子来实现状态模式下场景状态的跳转,首先来看看,下面有三个场景状态,首先是开始界面,显示完Logo后马上跳转到游戏主界面场景状态,点击开始游戏有跳转到战斗场景状态
创建三个场景分别命名为StartScene,MainScene,BattleScene。在StartScene场景里创建 GameLoop 游戏对象,并在其上挂载GameLoop脚本.
using UnityEngine;
public class GameLoop : MonoBehaviour {
private SceneStateController controller=null;
public void Awake()
{
DontDestroyOnLoad(this.gameObject);//跳转场景也不删除此游戏对象
}
// Use this for initialization
void Start () {
controller = new SceneStateController();
controller.SetState(new StartState(controller),false);//设置默认状态(初始状态)
}
// Update is called once per frame
void Update () {
controller.StateUpdate();
}
}
然后就是创建状态拥有者SceneController脚本
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneStateController
{
private ISceneState mState;
private AsyncOperation mAO;
//控制StateStart只调用一次
private bool mIsRunStart = false;
public void SetState(ISceneState state, bool isLpadScene=true)
{
if (mState != null)//如果当前有状态
{
mState.StateEnd();//让上一个场景状态做清理工作
}
mState = state;//设置状态
//isLpadScene是否需要加载场景,因为最开始的场景是不需要加载的
if (isLpadScene)
{
mAO = SceneManager.LoadSceneAsync(mState.SceneName);
mIsRunStart = false;
}
else
{
mState.StateStart();
mIsRunStart = true;
}
}
public void StateUpdate()
{
//正在加载场景的时候
if (mAO != null && mAO.isDone == false) return;
//场景加载完成 mAO.isDone == true如果场景加载完成才调用StateStart方法
if (mIsRunStart==false&&mAO != null && mAO.isDone == true)
{
mState.StateStart();
mIsRunStart = true;
}
if (mState != null)
{
mState.StateUpdata();//场景状态下每帧更新
}
}
}
下面就是各个状态的基类,
public class ISceneState
{
private string mSceneName;
protected SceneStateController mController;
public ISceneState(string sceneName,SceneStateController controller)
{
mSceneName = sceneName;
mController = controller;
}
public string SceneName
{
get { return mSceneName; }
}
//每次进入这个状态的时候调用
public virtual void StateStart() { }
//这个状态下每帧更新
public virtual void StateUpdata(){}
//退出这个状态的时候调用
public virtual void StateEnd() { }
}
下面是三个场景状态类继承ISceneState
using UnityEngine;
using UnityEngine.UI;
public class StartState : ISceneState
{
public StartState(SceneStateController controller) : base("StartScene", controller)
{
}
private Image mLogo;
private float mSmoothingTime = 0.5f;
private float mWaitTime = 2;
public override void StateStart()
{
mLogo = GameObject.Find("Logo").GetComponent<Image>();//查找开始面板
mLogo.color = Color.black;//初始设置为黑色
}
public override void StateUpdata()
{
mLogo.color = Color.Lerp(mLogo.color,Color.white, mSmoothingTime*Time.deltaTime);//从黑色跳转到白色
mWaitTime -= Time.deltaTime;
if (mWaitTime<=0)//两秒后跳转到第二个场景
{
mController.SetState(new MainState(mController));
}
}
}
using UnityEngine;
using UnityEngine.UI;
public class MainState : ISceneState
{
public MainState(SceneStateController controller) : base("MainScene", controller)
{
}
public override void StateStart()
{
GameObject.Find("StartButton").GetComponent<Button>().onClick.AddListener(OnClick); ;//给Button注册按钮事件
}
private void OnClick()
{
mController.SetState(new BattleState(mController));//跳转到第三个场景
}
}
public class BattleState : ISceneState
{
public BattleState( SceneStateController controller) : base("BattleScene", controller)
{
}
}
外观模式
外观模式定义:
为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
概述:
外观模式通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。
外观模式实现:
外观模式通过引入一个新的外观类(Facade)来实现该功能,外观类充当了软件系统中的“服务员”,它为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互。在外观模式中,那些需要交互的业务类被称为子系统(Subsystem)。如果没有外观类,那么每个客户类需要和多个子系统之间进行复杂的交互,系统的耦合度将很大,如图2(A)所示;而引入外观类之后,客户类只需要直接与外观类交互,客户类与子系统之间原有的复杂引用关系由外观类来实现,从而降低了系统的耦合度,如图2(B)所示。
外观模式中,一个子系统的外部与其内部的通信通过一个统一的外观类进行,外观类将客户类与子系统的内部复杂性分隔开,使得客户类只需要与外观角色打交道,而不需要与子系统内部的很多对象打交道。
代码实现
class SubSystemA
{
public void MethodA()
{
//业务实现代码
}
}
class SubSystemB
{
public void MethodB()
{
//业务实现代码
}
}
class SubSystemC
{
public void MethodC()
{
//业务实现代码
}
}
class Facade
{
private SubSystemA obj1 = new SubSystemA();
private SubSystemB obj2 = new SubSystemB();
private SubSystemC obj3 = new SubSystemC();
public void Method()
{
obj1.MethodA();
obj2.MethodB();
obj3.MethodC();
}
}
class Program
{
static void Main(string[] args)
{
Facade facade = new Facade();
facade.Method();
}
}
借鉴自:
深入浅出外观模式
设计模式学习笔记-状态模式
单例模式:
http://blog.csdn.net/carson_ho/article/details/52223097
http://blog.csdn.net/iblade/article/details/51107308
设计模式之中介者模式(Mediator):
http://www.cnblogs.com/BeyondAnyTime/archive/2012/08/30/2663922.html