java命令模式的实际应用

来源:互联网 发布:seo如何发外链 编辑:程序博客网 时间:2024/06/04 19:03

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. 命令处理器

命令接口

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

@FunctionalInterfacepublic 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);}

测试结果
这里写图片描述

1 0
原创粉丝点击