当前位置:首页 > 其他 > 正文内容

架构演化考虑总结(2)

邻居的猫1个月前 (12-09)其他1186

架构演化考虑总结(2)

—-–从指令形式中来探究处理依靠联系

在正式引进指令形式的概念之前,咱们先从简略的事例来逐渐演化咱们在书面上常见到的内容。

public interface ICommand
{
    void Execute();
}

public class PlayMusicCommand : ICommand
{
     public void Execute()
     {
        Debug.Log("你说家是仅有的城堡,跟着稻香一路奔驰~");
     }
}

var Start()
{
    var command = new PlayMusicCommand();
    command.Execute();
}

这儿咱们界说一个指令接口,假如是指令,必定要完结的一个履行办法。

PlayMusicCommand 完结接口,此指令的效果便是播映Jay的《稻香》,假如想完结播映音乐功用,直接履行对应指令的办法即可!

体现出指令的实质,咱们把要所作的内容或许操控逻辑封装到一同,当咱们需求履行它时分,下达履行指令的办法即可!

来看AI对指令形式的介绍:
image

上面小事例正对应对“操作逻辑”进行封装,提炼成指令,那么这样对操作逻辑进行封装有什么优点呢?

清楚明了的优点之一便是便利办理,逻辑明晰。进行杂乱逻辑开发时分,咱们正式把它尽可能提炼封装成办法,为的便是便利办理,而指令形式是对逻辑代码再高一个层次的封装,也便是说从办法笼统成类,明显愈加便于办理和复用。

运用指令形式下降杂乱逻辑的开发调试难度,你排查一个几百行的大函数的bug必定比封装拆分红几个函数或许是几个对应的指令的情况要费事。比方咱们需求进行某个杂乱操作,可是咱们对它进行拆分封装,分红几个Command来履行,这样既能够分发给几个搭档一同协作杂乱逻辑开发,没有与中心逻辑操控脚本发生过多的耦合

当然另一方面分管了操控脚本Controller的操控压力,使其没那么臃肿。

public interface ICommand
{
    void Execute();
}

public class ACommand
{
    public void Execute()
    {
        Debug.Log("Execute A Command");
    }
}

public class BCommand
{
    public void Execute()
    {
        Debug.Log("Execute B Command");
    }
}

public class CCommand
{
    public void Execute()
    {
        Debug.Log("Execute C Command");
    }
}

void Start()
{
    var commands = new List<ICommand>();
    commands.Add(new ACommand());
    commands.Add(new BCommand());
    commands.Add(new CCommand());
  
    commands.ForEach(c=>c.Execute());
}

指令形式–带着参数

假如咱们要履行需求参数才干进行的指令呢?

好,接下来咱们完结能够带着参数的指令,十分简略,只需求给履行的指令中声明参数即可!

interface ICommand
{
    void Execute();
}
public class BuyGoodsCommand : ICommand
{
    private int goodsId;
    private int goodsCount;
    public BuyGoodsCommand(int id,int count)
    {
        goodsId = id;
        goodsCount = count;
    }
    public void Execute()
    {
        Debug.Log($"购买了id为{goodsId}的产品{goodsCount}个");
        //履行相关的购买逻辑
        //......
    }
}

public class Test : MonoBehaviour
{
    private void Start()
    {
        var buyGoodsCommand = new BuyGoodsCommand(1, 15);
        buyGoodsCommand.Execute();
    }
}

指令形式–吊销功用

接下来接着向指令形式的功用完结跨进,在刚触摸指令形式的时分,会猎奇的想到,已然把指令都封装好一步步履行了,那能不能吊销现已履行好的行为呢?笔者也是在学习到指令形式之后才联想到各种修改东西的Ctrl+Z的效果的完结思路。那咱们往指令形式中增加一个吊销功用。

当然要履行吊销指令,要有个容器来存储现已履行的指令,这儿运用的是List,也能够用Stack

和Queue,当然运用栈就能够完结Ctrl + Z的逐渐吊销功用了!

interface ICommand
{
    void Execute();
    void Undo();
}
public class BuyGoodsCommand : ICommand
{
    private int goodsId;
    private int goodsCount;
    public BuyGoodsCommand(int id,int count)
    {
        goodsId = id;
        goodsCount = count;
    }
    public void Execute()
    {
        Debug.Log($"购买了id为{goodsId}的产品{goodsCount}个");
        //履行相关的购买逻辑
        //......
    }

    public void Undo()
    {
        Debug.Log($"方才购买的id为{goodsId}的产品{goodsCount}个,现已悉数退货!");
        //履行相关的退货操作
        //如库存++
        //玩家金币++
    }
}

public class Test : MonoBehaviour
{
    private void Start()
    {
        var commands = new List<BuyGoodsCommand>();
        commands.Add(new BuyGoodsCommand(1, 15));
        commands.Add(new BuyGoodsCommand(5, 2));

        //履行购买
        commands.ForEach(command => command.Execute());

        //5号物品不想要了 退货
        commands[1].Undo();
    }
}

指令形式–指令和履行别离

这儿和上一篇所陈说的依靠联系大致相同,咱们把指令从一个目标降级成办法来看。

咱们常常进行的办法调用这种行为,便是指令和履行未别离的一个比如。即办法调用必定办法中的逻辑履行。

void DoSomethingCommand()
{
    Debug.Log("指令履行了!");
}
void Start()
{
    DoSomethingCommand();
}

那么指令和履行分隔是怎么样的呢?

咱们能够运用托付来完结,时刻和空间上的别离。

 public class A : MonoBehaviour
 {
     B b;
     void Start()
     {
         b = transform.Find("Animation").GetComponent<B>();

         // 注册完结的事情
         b.OnDoSomethingDone += DoSomethingCommand;
     }
     void DoSomethingCommand()
     {
         Debug.Log("指令履行了!");
     }
 }

public class B : MonoBehaviour
{
    // 界说托付
    public Action OnDoSomethingDone = ()=>{};

    //当动画播映结束后调用
    public void DoSomething()
    {
        //触发托付中的函数履行
        OnDoSomethingDone();
    }
}

这样即将履行的指令DoSomethingCommand,会在特定机遇(时刻上别离)由别的一个脚本(空间上别离)调用履行,完结时空别离。

好,咱们现已在办法层面表述出指令的别离,现在咱们回到类这个层面,将Command的声明和履行进行别离。

这就需求一个对托付进行另一层的封装运用,这儿是用托付(能够简略理解为函数容器),存储的是函数(command简化为办法层面),能够运用。对应的将指令晋级晋级成目标,为此也要对

托付进行“晋级”,这儿参阅QFramWork的自界说的事情机制。

自界说事情机制

咱们期望它事情机制具有功用:发送事情功用和主动刊出功用。

发送事情是有必要的,而主动刊出功用要的是当注册事情监听的GameObject的目标Destroy之后,要刊出对事情的监听功用。

现在依照这样的要求来完结接口:

 public interface ITypeEventSystem
 {
     /// <summary>
     /// 发送事情
     /// </summary>
     /// <typeparam name="T"></typeparam>
     void Send<T>() where T : new ();

     void Send<T>(T e);

     IUnRegister Register<T>(Action<T> onEvent);

     /// <summary>
     /// 刊出事情
     /// </summary>
     /// <param name="onEvent"></param>
     /// <typeparam name="T"></typeparam>
     void UnRegister<T>(Action<T> onEvent);
 }
//刊出机制
 public interface IUnRegister
 {
     void UnRegister();
 }

来侧重完结主动刊出机制:

咱们来声明一个类,来详细履行刊出事情的功用:

public class TypeEventSystemUnRegister<T> : IUnRegister
{
    //持有事情机制引证
    public ITypeEventSystem TypeEventSystem { get; set; }
    
    //持有待刊出的托付
    public Action<T> OnEvent {get;set;}
    
    //详细的刊出机办法
    public void UnRegister()
    {
        //详细便是调用事情机制(体系)对应的办法,刊出掉指定的函数 (OnEvent)
        TypeEventSystem.UnRegister(OnEvent);
        
        TypeEventSystem = null;
            OnEvent = null;
    }
}

当然刊出机遇是在当GameObjet毁掉时分,为此需求一个“触发器”,其挂载在注册事情的GameObject上,当检测到Destroy时分进行触发。

来完结对应的触发器:

/// <summary>
/// 刊出事情的触发器
/// </summary>
public class UnRegisterOnDestroyTrigger : MonoBehaviour
{
    private HashSet<IUnRegister> mUnRegisters = new HashSet<IUnRegister>();

    public void AddUnRegister(IUnRegister unRegister)
    {
        mUnRegisters.Add(unRegister);
    }

    private void OnDestroy()
    {
        foreach (var unRegister in mUnRegisters)
        {
            unRegister.UnRegister();
        }

        mUnRegisters.Clear();
    }
}

来对刊出机制的接口拓宽功用,便利在注册事情时分调用一个办法,经过这个办法调用直接将上段代码所示的刊出机制的触发器挂载在GameObject上。

public static class UnRegisterExtension
{
    public static void UnRegisterWhenGameObjectDestroyed(this IUnRegister unRegister, GameObject gameObject)
    {
        var trigger = gameObject.GetComponent<UnRegisterOnDestroyTrigger>();

        if (!trigger)
        {
            trigger = gameObject.AddComponent<UnRegisterOnDestroyTrigger>();
        }

        trigger.AddUnRegister(unRegister);
    }
}

至此,当咱们在运用时分调用一下’UnRegisterWhenGameObjectDestroyed‘办法,将会挂载Tirgger,当物体毁掉时分会触发,完结主动刊出事情,有用的确保了在运用Unity中托付的注册和刊出成对呈现的特征,避免托付中呈现空指针。

好,完结完结主动刊出事情机制,持续完结事情的注册和调用机制。

public class TypeEventSystem : ITypeEventSystem
{
    //运用依靠倒转准则
    interface IRegistrations
    {

    }

    class Registrations<T> : IRegistrations
    {
        public Action<T> OnEvent = obj => { };
    }
//依据事情的类型来存储 对应的事情Action<T> 被封装成类 以接口类型存储 
    private Dictionary<Type, IRegistrations> mEventRegistrations = new Dictionary<Type, IRegistrations>();
    
    public void Send<T>() where T : new()
    {
        var e = new T();
        Send<T>(e);
    }
    //详细发送机制 调用机制
    public void Send<T>(T e)
    {
        var type = typeof(T);
        IRegistrations eventRegistrations;

        if (mEventRegistrations.TryGetValue(type, out eventRegistrations))
        {
            //详细调用 “解压 降维” 调用托付
            (eventRegistrations as Registrations<T>)?.OnEvent.Invoke(e);
        }
    }
    
    //注册完结
     public IUnRegister Register<T>(Action<T> onEvent)
     {
         var type = typeof(T);
         //详细存储 “加压 升维” 向托付中增加函数
         IRegistrations eventRegistrations;

         //判别存储的事情类型存在否
         if (mEventRegistrations.TryGetValue(type, out eventRegistrations))
         {

         }
         else
         {
             //不存在就增加一个
             eventRegistrations = new Registrations<T>();
             mEventRegistrations.Add(type,eventRegistrations);
         }

         //假如存在就 解压 增加到“解压”好的事情机制中
         (eventRegistrations as Registrations<T>).OnEvent += onEvent;

         //回来刊出目标需求的数据(引证)实例
         // 能够不经过结构函数来对共有拜访目标初始化赋值
         return new TypeEventSystemUnRegister<T>()
         {
             OnEvent = onEvent,
             TypeEventSystem = this
         };
     }
    //刊出办法的详细完结
    public void UnRegister<T>(Action<T> onEvent)
    {
        var type = typeof(T);
        IRegistrations eventRegistrations;

        if (mEventRegistrations.TryGetValue(type,out eventRegistrations))
        {
            (eventRegistrations as Registrations<T>).OnEvent -= onEvent;
        }
    }
    
}

至此自界说的事情机制完结结束!

假如感兴趣,重视其对应的测验事例展现,(将在独自一篇博客介绍此事情机制)

持续推动,运用此机制来完结Command的时空别离:

public interface ICommand
{
    void Execute();
}

public class SayHelloCommand
{
    public void Execute()
    {
        // 履行
        Debug.Log("Say Hello");
    }
}

void Start()
{
    // 指令
    var command = new SayHelloCommand();

    command.Execute();


    mTypeEventSystem = new TypeEventSystem();
    
   mTypeEventSystem.Register<ICommand>(Execute).UnRegisterWhenGameObjectDestroyed(gameObject);

    // 指令 运用Command目标注册
   mTypeEventSystem.Send<Icommand>(new SayHelloCommand());
}

那么比照三种完结办法发现什么?

  • 办法:调用即履行!没有别离
  • 事情机制:履行在事情注册中完结 有别离
  • Command:履行在Command内部完结 有别离

明显,Command对指令和履行的别离程度介于办法和事情机制之间。

要点比照事情机制,在完结自界说办法之前,笔者现已点到托付存储的办法,和在运用封装后托付(自界说事情)能够存储类(指令实例),尽管都是经过托付来存储履行办法,运用Command更为自在一些,能够在自界说的方位和机遇履行,而事情机制一般至少需求经过两个目标才干完好运用。

先写到这儿吧!

下面咱们持续探究指令形式在架构演化中的效果,持续挨近咱们学习中触摸到的Command形式!

谢谢各位能和我一同来探究项目架构规划演化!

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=702

分享给朋友:

“架构演化考虑总结(2)” 的相关文章

Prime2_解法二:openssl解密凭证

Prime2_解法二:openssl解密凭证

Prime2_解法二:openssl解密凭证 本博客供给的一切信息仅供学习和研讨意图,旨在进步读者的网络安全意识和技能才能。请在合法合规的前提下运用本文中供给的任何技能、办法或东西。假如您挑选运用本博客中的任何信息进行非法活动,您将单独承当悉数法律责任。本博客清晰表明不支撑、不鼓舞也不参加任何方法的...

二进制装置Kubernetes(k8s)v1.31.1

二进制装置Kubernetes(k8s)v1.31.1

二进制装置Kubernetes(k8s)v1.31.1 介绍 https://github.com/cby-chen/Kubernetes 开源不易,帮助点个star,谢谢了 kubernetes(k8s)二进制高可用装置布置,支撑IPv4+IPv6双栈。 强烈建议在Github上检查文档 !!!...

为同伴点赞:Vector Shop 上架即登付费榜

为同伴点赞:Vector Shop 上架即登付费榜

好同伴 CYG 历时两年独立研制的图形规划软件 VectorShop ,因为产品规划清新,操作简略,功用有用等特性,一上架就登上分类收费榜 第11名 ,且排名每天还在上升中。 VectorShop 是一款独具特色的矢量绘图软件,专为规划师供给直观而强壮的功用,助力高效创造精巧的矢量图形和图标。 经...

Astro v5 x DevNow

Astro v5 x DevNow

先介绍下 DevNow DevNow Github 体会网站 DevNow 是一个精简的开源技能博客项目模版,支撑 Vercel 一键布置,支撑谈论、查找等功能,欢迎我们体会。一起也支撑 Follow、 RSS 订阅,欢迎我们订阅。 现在承载着: 我的Blog:一些相关的技能文章和个人日子记载。 W...

【知识点】一文讲清动态规划的实质

【知识点】一文讲清动态规划的实质

一文讲清动态规划的实质 动态规划 Dynamic Programming (DP) 是算法范畴的中心思维之一,却一起也是让许多学习者感到扎手的难点之一。动态规划的难点在于它不是简略的数学推导,也不单纯检测人们的程序规划才能,而更像是一种从思维办法到问题建模的一次深入练习。 本文将从动态规划的界说动身...

区块链的共识机制,深入解析区块链的共识机制

区块链的共识机制,深入解析区块链的共识机制

区块链的共识机制是指通过特定的算法和规则,让网络中的节点就交易的有效性达成一致,从而保证区块链网络的安全性和可靠性。不同的区块链系统采用了不同的共识机制,常见的共识机制包括:1. 工作量证明(Proof of Work,PoW):这是比特币最早使用的共识机制。节点通过解决计算难题来竞争记账权,计算难...