设计模式之状态模式与外观模式

状态模式代码原型

解决的问题:
  主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化。

模式中的角色
  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状态,这就是状态直接的相互切换。


image.png

场景状态跳转栗子

下面我举例子来实现状态模式下场景状态的跳转,首先来看看,下面有三个场景状态,首先是开始界面,显示完Logo后马上跳转到游戏主界面场景状态,点击开始游戏有跳转到战斗场景状态


image.png

创建三个场景分别命名为StartScene,MainScene,BattleScene。在StartScene场景里创建 GameLoop 游戏对象,并在其上挂载GameLoop脚本.

image.png
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)
    {
    }
}
fdsfsfgr.gif

外观模式

外观模式定义:
为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
概述:
外观模式通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。

外观模式实现:
外观模式通过引入一个新的外观类(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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容