java命令模式的实际应用

java在实际该项目中,用户会进行各种各样的请求操作,现对该问题处理方法进行探讨。

if-else语句或者switch-case语句

具体内容

在实际开发项目中对要功能模块进行管理,通过对输入的某个值的判断确定用户想要做什么,比如输入1表示做事件1,输入2表示做事件2.传统的实现方式可用if-else或者switch-case语句来完成。

相关代码

//if-else语句下的处理方式
//表示用户的请求
int cmd=1;
if(cmd==1){
    //做事件1

}else if(cmd==2){
    //做事件2
}
//其他
//switch-case语句下的处理方式
switch (cmd) {
case 1:{
    //做事件1
    break;
}
case 2:{
    //做事件2
    break;
}
default://........

    break;
}

评价

该方法是一般最初能想到的也是最直接的方法,但此方法存在一下缺陷。

  1. 没有体现出面向对象的特征。
  2. 会增大编程复杂度,降低程序的可读性,如果功能过多,则处理语句会很长。
  3. 降低程序的性能:运行相对靠后的代码块时必须把前面的判断也访问到,增加了运行时间。
  4. 增大开发维护的成本:如果要增加一个功能,必须到源代码中去寻找,修改也要在源代码中去修改。而在实际项目中修改源代码是不好的。

命令模式的搭建

以上论述了用if-else,swithc-case语句会的缺点,改造可采用命令模式。
命令模式主要有一下三个要素
1. 命令接口
2. 命令实现类
3. 命令处理器

命令接口

命令接口
为了体现面向对象的抽象性,以及接口和实现分离的原则,对处理事件的过程分装为一个抽象的接口,定义如下。

@FunctionalInterface
public interface Command {

    /**
     * 命令处理接口 
     * @param obj 处理数据
     * @return 处理结果
     */
    Object handle(Object obj);
}

传入一个Object类型参数,得到返回结果,具体的过程由实现类自己实现。

命令实现类

有了接口就要有实现该接口的具体类,下面举两个例子。

/**
 * 实现功能:打印传入的参数
 *
 */
public class Print implements Command {

    @Override
    public Object handle(Object obj) {
        System.out.println("你传入的是:"+obj);
        return null;
    }

}
/**
 * 实现功能 :输出一句话"什么也不做"
 *
 */
public class None implements Command {

    @Override
    public Object handle(Object obj) {
        System.out.println("什么也不做");
        return null;
    }

}

命令处理器

有了接口和实现类,下面要做的就是建立一个类来处理该接口,要想实现对应的功能,必须要知道Command接口对应的是哪个类的对象,为此传入一个String类型表示类名的参数,并通过反射来获取该类的对象。

public class CommandHandler {

    /**
     * 处理数据的方法
     * @param className 类名
     * @param obj 处理数据
     * @return 结果
     */
    public static Object handle(String className,Object obj)
    {
        try {
            //加载对应的Command实现类的对象
            Command c=(Command) Class.forName(className).newInstance();
            return c.handle(obj);
        } catch (Exception e){
        }
        return null;
    }

}

这里将处理方法设为公开静态方便调用,传入类名和具体数据,通过反射机制加载Class对象,并生成具体对象并转为Command类型,这里任何异常都不处理,最后通过Command对象调用方法返回结果。

现在可以进行简单的测试

public class Main {

    public static void main(String[] args) {
        CommandHandler.handle("Print", "123");
        CommandHandler.handle("None", "123");
    }

}

测试结果
这里写图片描述

至此,命令模式的基本框架基本搞定,但该方法存在许多问题,将在下面的叙述中进行优化。


命令模式的优化

使用xml文档

使用xml文档的必要性
1.以上通过反射来加载类,我们发现每次都要进行反射获取Class对象并且调用newInstance()方法生成一个对象,实际上我们只需要对应的方法即可,无须每次都要加载。
2.每次传入的参数中必须包含一个类的全类名,这样对用户来说不是很方便。
基于此,我们可以将所有要加载的类名事先写入xml文档,在程序初始化时读取文件,把所有的类名全部获取一个Command对象并保存,用对应的字符串作为关键字进行hash存取,这里的字符串即可当作用户输入的代表命令的字符串。
xml文档需要命令字符串和类名,可按一下格式定义,也可以自己定义。关于xml文档的知识这里不再详述。

<command>
    <name></name>
    <classname></classname>
</command>

继续拿本前面的程序来说明,我们可以建立如下的文档

<?xml version="1.0" encoding="utf-8"?>

<body>

    <command>
        <name>1</name>
        <classname>Print</classname>
    </command>

    <command>
        <name>2</name>
        <classname>None</classname>
    </command>

</body> 

用map进行保存

/**命令集到命令对象的映射*/
private static HashMap<String, Command> commands;

初始化时加载该xml文档,把数据读入到commands中

/**
     * 加载该类时完成对commands的初始化
     */
    static {

        // 标志 超时则退出
        int flag = 0;
        while (flag < 5) {
            try {
                // 定义文档解析器
                DocumentBuilder buidler = DocumentBuilderFactory.newInstance()
                        .newDocumentBuilder();
                // 获取解析后的文档对象
                Document doc = buidler.parse("main.xml");
                // 获取根部元素
                Element root = doc.getDocumentElement();
                // 加载命令
                commands = new HashMap<String, Command>(root.getChildNodes()
                        .getLength()*2+1);
                // 读取每个结点
                for (int i = 0; i < root.getChildNodes().getLength(); i++) {
                    Node e = root.getChildNodes().item(i);
                    // 找到command结点
                    if (e instanceof Element) {
                        Element elem = (Element) e;
                        if ("command".equals(elem.getNodeName())) {
                            NodeList nl = elem.getChildNodes();
                            // 处理每个command节点
                            String name = null, classname = null;
                            for (int j = 0; j < nl.getLength(); j++) {
                                Node n = nl.item(j);
                                if (n instanceof Element) {
                                    Element elem1 = (Element) n;

                                    // 获取name值
                                    if ("name".equals(elem1.getTagName())) {
                                        name = ((Text) elem1.getFirstChild())
                                                .getData().trim();
                                    }

                                    // 获取classname值
                                    else if ("classname".equals(elem1
                                            .getTagName())) {
                                        classname = ((Text) elem1
                                                .getFirstChild()).getData();
                                    }
                                }
                            }

                            // 把结果加入集合中
                            if (name != null && classname != null) {
                                //加载Command命令
                                Command c = (Command) Class.forName(classname).newInstance();
                                //放入集合中
                                commands.put(name, c);
                            }
                        }

                    }
                }
                break;
            } catch (Exception e) {
                flag++;
                // 设置超过五次则系统停止
                if (flag == 5) {
                    System.out.println("命令系统异常");
                    //退出系统
                    System.exit(-1);
                }
            }
        }

        if(commands==null)
        {
            System.out.println("命令系统异常");
            //退出系统
            System.exit(-1);
        }

    }

处理方法的代码优化

/**
     * 处理数据的方法
     * @param cmd 命令字符串
     * @param obj 处理数据
     * @return 结果
     */
    public static Object handle(String cmd,Object obj)
    {
        try {
            //查找map 获取对应的Command实现类的对象
            return commands.get(cmd).handle(obj);
        } catch (Exception e){
        }
        return null;
    }

测试代码

public static void main(String[] args) {
        //1对应的是打印
        CommandHandler2.handle("1", "123");
        //2对应什么也不做
        CommandHandler2.handle("2", "123");
        //3无效
        CommandHandler2.handle("3", "123");
    }

测试结果
这里写图片描述

这样,只要传入一个命令字符串即可,无须传入类名,每个类也只会加载一次生成一个对象。当需要添加一个命令时,只需添加一个类实现Command接口,并且完成未实现的方法,再把命令字符串和类名在xml文档中进行注册即可。

自定义Message对象

在实际开发中,这样传入参数还是有些麻烦,而且用户很多时候用户有多个请求时间,所以这样的结构还是有些问题,我们在这里定义一个Message对象分装用户的所有请求,本质上是一个个的命令字符串到数据的键对,这里用map存取。

public final class Message {


    /**表示信息的集合*/
    private Map<String,Object> msg=new HashMap<String, Object>();

    /**
     * 添加一条信息
     * @param str   信息对应指令
     * @param obj   信息内容
     */
    public void put(String str,Object obj)
    {
        msg.put(str, obj);
    }

    /**
     * 判断消息为空
     * @return true表示消息为空  false 表示有内容
     */
    public boolean isEmpty()
    {
        return msg.isEmpty();
    }

    /**
     * 获取信息长度
     * @return 信息长度
     */
    public int Size() {
        return msg.size();
    }

    /**
     * 根据指令获得信息内容
     * @param str 指令
     * @return 信息内容
     */
    public Object getParameter(String str)
    {
        return msg.get(str);
    }

    /**
     * 
     * @return 命令字符串集
     */
    public Set<String> KetSet()
    {
        return msg.keySet();
    }
}

修改处理的方法

/**
     * 处理数据的方法
     * @param message 处理信息
     * @return 结果
     */
    public static Message handle(Message message)
    {
        Message r=new Message();
        try {
            for(String e:message.KetSet())
            {
                //获取每个命令和对应数据 并获得结果
                r.put(e, commands.get(e).handle(message.getParameter(e)));
            }

        } catch (Exception e){
        }
        return r;
    }

测试

public static void main(String[] args) {
    //生成一个Message
    Message message=new Message();

    message.put("1", "123");
    message.put("2", "123");
    message.put("3", "123");

    CommandHandler3.handle(message);
}

测试结果
这里写图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值