设计模式之游戏--命令模式

本文通过一个兵营训练士兵的例子介绍了命令模式在游戏开发中的应用。在非命令模式下,UI类和兵营类高度耦合,不符合开闭原则。命令模式通过将请求封装为对象,实现了调用者与接收者的解耦,增强了系统的可扩展性和可撤销功能。文章分析了命令模式的优点,如封装性和扩展性,并指出其可能带来的类数量增多的问题。最后,通过重构代码展示了命令模式的具体实现,使得兵营训练命令更加灵活和易于管理。
摘要由CSDN通过智能技术生成

设计模式之游戏--命令模式

引入问题

在软件开发过程中,请求-响应模式的功能是一个很常见的功能,比如在我们的策略游戏中,通常有一个兵营系统,当我们点击某个兵营训练时就训练出某种士兵。如果是非命令模式下,你可能会做出如下模拟:

首先新建一个兵营类:

public class Camp
{
    public void Make_Warrior_Soldier()
    {
        MonoBehaviour.print("开始制造战士士兵");
    }

    public void Make_Archer_Soldier_Command()
    {
        MonoBehaviour.print("开始制造弓箭手士兵");
    }

    public void Make_Magic_Soldier()
    {

        MonoBehaviour.print("开始制造魔法师士兵");
    }
}

然后新建一个UI类,并持有一个兵营的引用

public class UI 
{
    Camp camp;
    public UI(Camp camp)
    {
        this.camp = camp;
    }

    public void Make_Warrior_Soldier()
    {
        camp.Make_Warrior_Soldier();
    }

    public void Make_Archer_Soldier()
    {
        camp.Make_Archer_Soldier_Command();
    }

    public void Make_Magic_Soldier()
    {
        camp.Make_Magic_Soldier();
    }
}

最后模拟一下向不同的兵营的添加不同的按钮方法:

public class Client_1 : MonoBehaviour
{

    private void Start()
    {
        Camp camp=new Camp();
        UI uI = new UI(camp);

        //点击战士兵营训练战士
        uI.Make_Warrior_Soldier();
        //点击弓箭手兵营训练弓箭手
        uI.Make_Archer_Soldier_Command();
        //点击魔法师兵营训练魔法师
        uI.Make_Magic_Soldier();
    }

}

我们很快的实现了我们的功能,但我们可以看出Camp类和UI类完全的耦合在一起了。
假如我们新增了其它兵种就需要做出如下修改:

1、修改Camp对象,增加若干个的Make_其它兵种的方法来制造不同的士兵。
2、修改UI对象,也添加若干个对应的Make_其它兵种的方法。
这明显不符合我们的开闭原则。而且当我们需要撤销当前命令或者回退命令,亦或者对已存在的命令列表就行一些操作时,这种设计显然不符合我们的需求。所以我们的命令模式由此而生。

定义

将请求封装成一个对象,从而让用户使用不同的请求把客户端参数化,以及支持可撤销和恢复的功能。

UML

在这里插入图片描述
Receiver(接收者):负责具体实施和执行一个请求。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。

Invoker(调用者):负责调用命令对象执行请求。

Command(抽象命令):定义命令的接口,声明执行亦或者其它关于命令的方法。

ConcreteCommand(具体命令):实现命令接口,是抽象的具体实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

Client(客户端):创建一个具体命令对象并设定该命令对象的接收者。

具体示例

下面我们以上面的兵营系统为例,实现命令模式。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

//兵营系统-Invoker(调用者)

public  class CampSystem_Invoker
{
    protected List<Command> commands = new List<Command>();
    
    public void AddCommand(Command command)
    {
        commands.Add(command);
        command.Execute();
    }

    public void Back_Command()
    {
        if (commands.Count<=0)
        {
            return;
        }
        commands[commands.Count - 1].Cancel();
        commands.RemoveAt(commands.Count-1);

    }

}


//兵营-Receiver(接收者)
public class Camp_Receiver
{
    public void Make_Warrior_Soldier()
    {
        MonoBehaviour.print("开始制造战士士兵");
    }

    public void Make_Archer_Soldier_Command()
    {
        MonoBehaviour.print("开始制造弓箭手士兵");
    }

    public void Make_Magic_Soldier()
    {

        MonoBehaviour.print("开始制造魔法师士兵");
    }

    public void Cancel_Warrior_Soldier()
    {

        MonoBehaviour.print("取消制造战士士兵");
    }

    public void Cancel_Archer_Soldier_Command()
    {

        MonoBehaviour.print("取消制造弓箭手士兵");
    }

    public void Cancel_Magic_Soldier()
    {
        MonoBehaviour.print("取消制造魔法师士兵");
    }
}


public abstract class Command
{
   protected Camp_Receiver camp_Receiver;
    public Command(Camp_Receiver receiver)
    {
        camp_Receiver = receiver;
    }
    public abstract void Execute();
    public abstract void Cancel();

}
//抽象命令
public class Warrior_Soldier_Command : Command
{
    public Warrior_Soldier_Command(Camp_Receiver receiver):base(receiver)
    {
     
    }
    public override void Execute()
    {
        camp_Receiver.Make_Warrior_Soldier();
    }

    public override void Cancel()
    {
        camp_Receiver.Cancel_Warrior_Soldier();
    }

  
}

public class Archer_Soldier_Command : Command
{
    public Archer_Soldier_Command(Camp_Receiver receiver) : base(receiver)
    {
    }

   

    public override void Execute()
    {

        camp_Receiver.Make_Archer_Soldier_Command();
    }

    public override void Cancel()
    {
        camp_Receiver.Cancel_Archer_Soldier_Command();
    }
}

public class Magic_Solier_Command : Command
{
    public Magic_Solier_Command(Camp_Receiver receiver) : base(receiver)
    {

    }
 
    public override void Execute()
    {

        camp_Receiver.Make_Magic_Soldier();
    }
    public override void Cancel()
    {
        camp_Receiver.Cancel_Magic_Soldier();
    }


}


public class Client_1:MonoBehaviour
    {

    private void Start()
    {
        Camp_Receiver camp_Receiver = new Camp_Receiver();
        
        Command warrior = new Warrior_Soldier_Command(camp_Receiver);
        Command archer = new Archer_Soldier_Command(camp_Receiver);
        Command magic = new Magic_Solier_Command(camp_Receiver);

        CampSystem_Invoker campSystem_Invoker = new CampSystem_Invoker();
        campSystem_Invoker.AddCommand(warrior);
        campSystem_Invoker.AddCommand(archer);
        campSystem_Invoker.AddCommand(magic);
        campSystem_Invoker.Back_Command();
    }

}

}

**在这里插入图片描述**

分析:通过最后的客户端代码我们发现,通过使用命令模式我们把一条命令的执行分为了三步
campSystem_Invoker(调用者)–>Command (命令)—> camp_Receiver(接收执行者),降低了调用者与执行者之间的耦合度,使双方不必关心对方是如何操作的。然后值得一提的是,在本例中,调用者campSystem兵营系统在实际的开发中更多的时候会是我们的兵营camp,简单来说就是兵营camp既是我们的调用者也是我们的接收者。因为我们的兵营都具有唯一性,campSystem不可能维护所有的兵营实例,但说每个兵营维护自己的Command 集是轻松且方便的。所以说设计模式我们不能死板的套用,我们更应该考虑实际情况进行变动使之符合我们的实际要求。

总结

优点:
1.封装性很好:每个命令都被封装起来,对于客户端来说,需要什么功能就去调用相应的命令,而无需知道命令具体是怎么执行的。

2.扩展性很好,在命令模式中,新建命令只需新建命令脚本,符合我们的开闭原则,并且在接收者类中一般会对操作进行最基本的封装,代码的复用性很好。

缺点:
恩…,可能会类爆炸!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值